在WPF中,一个依赖属性值依赖于另一个属性值是否正确?

本文关键字:属性 依赖于 另一个 是否 依赖 一个 WPF | 更新日期: 2023-09-27 18:17:49

我有一个包含2个数据的类:

public class Settings
{
    EnumType Mode;
    float Rate;
}

属性Rate的值取决于属性Mode的值。我使用CoerceValue函数来更新以确保Rate值在任何时候都是正确的。

属性以只读模式绑定到UI(一种方式),因为我想在写时做一些额外的处理。所以我在UI控件上创建了一个事件来知道Rate属性何时改变。

在我的窗口UI被绑定到一个静态变量SelectedSettings

我的问题如下:

当我改变SelectedSettings的值时(使用另一个settings类),而不是在UI中加载新的设置,它执行以下操作:

  • 在UI中设置新的Mode
  • 上述操作启动"coerceValue"进程,修改"Rate"值。
  • 速率值的修改触发事件。
  • 触发事件将rate的新值写入SelectedSettings
  • 设置新的Rate值(现在不正确)

我做错了什么?我对依赖属性和强制系统的使用是否无效?

编辑:这里有更多关于我实际状态的信息

我创建了一个用户控件来显示两个设置选项

public partial class EncodingQualitySliderControl : UserControl
{
    public static readonly DependencyProperty BitrateProperty = DependencyProperty.Register(
        "Bitrate",
        typeof(double),
        typeof(EncodingQualitySliderControl),
        new FrameworkPropertyMetadata(new PropertyChangedCallback(EncodingQualitySliderControl.OnBitrateValueChanged), new CoerceValueCallback(EncodingQualitySliderControl.CoerceBitrateValue)));
    public static readonly DependencyProperty EncodingModeProperty = DependencyProperty.Register(
        "EncodingMode",
        typeof(EncodingMode),
        typeof(EncodingQualitySliderControl),
        new PropertyMetadata(new PropertyChangedCallback(EncodingQualitySliderControl.OnEncodingModeValueChanged)));
    public event EventHandler<double> BitrateValueChanged;
    public EncodingQualitySliderControl()
    {
        this.InitializeComponent();
        this.CoerceValue(EncodingQualitySliderControl.BitrateProperty);
        Debug.Assert(this.slider != null);
        this.slider.ValueChanged += this.Slider_ValueChanged;
    }
    public EncodingMode EncodingMode
    {
        get
        {
            return (EncodingMode)this.GetValue(EncodingQualitySliderControl.EncodingModeProperty);
        }
        set
        {
            this.SetCurrentValue(EncodingQualitySliderControl.EncodingModeProperty, value);
        }
    }
    public double Bitrate
    {
        get
        {
            return (double)this.GetValue(EncodingQualitySliderControl.BitrateProperty);
        }
        set
        {
            this.SetCurrentValue(EncodingQualitySliderControl.BitrateProperty, this.GetNearestTickValue(value));
        }
    }
    private static void OnBitrateValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs eventArgs)
    {
        EncodingQualitySliderControl encodingQualitySliderControl = sender as EncodingQualitySliderControl;
        encodingQualitySliderControl.slider.Value = (double)eventArgs.NewValue;
    }
    private static object CoerceBitrateValue(DependencyObject sender, object basevalue)
    {
        EncodingQualitySliderControl encodingQualitySliderControl = sender as EncodingQualitySliderControl;
        return encodingQualitySliderControl.GetNearestTickValue((double)basevalue);
    }
    private static void OnEncodingModeValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs eventArgs)
    {
        EncodingQualitySliderControl encodingQualitySliderControl = sender as EncodingQualitySliderControl;
        Slider sliderControl = encodingQualitySliderControl.slider;
        // ... Some code that change the user control depending on the new mode.
        encodingQualitySliderControl.CoerceValue(EncodingQualitySliderControl.BitrateProperty);
        // Send ValueChanged in case of bitrate value change from coerce value.          
        encodingQualitySliderControl.BitrateValueChanged?.Invoke(encodingQualitySliderControl, encodingQualitySliderControl.Bitrate);
    }
    private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        this.SetCurrentValue(EncodingQualitySliderControl.BitrateProperty, e.NewValue);
        // Only send the bitrate value changed event if the value change come from the slider.
        this.BitrateValueChanged?.Invoke(this, e.NewValue);
    }
}

然后我有一个类与我的设置数据:

public class ConversionPreset : INotifyPropertyChanged
{
    private EncodingMode mode;
    private double bitrate;
    public event PropertyChangedEventHandler PropertyChanged;
    [XmlAttribute]
    public EncodingMode Mode
    {
        get
        {
            return this.mode;
        }
        set
        {
            this.mode = value;
            this.OnPropertyChanged();
        }
    }
    [XmlAttribute]
    public double Bitrate
    {
        get
        {
            return this.bitrate;
        }
        set
        {
            this.bitrate= value;
            this.OnPropertyChanged();
        }
    }
}

然后在我的设置类和我的用户控件之间有一个数据绑定。

<controls:EncodingQualitySliderControl x:Name="EncodingQualitySlider" BitrateValueChanged="EncodingQualitySlider_ValueChanged"
                    EncodingMode="{Binding SelectedPreset.Mode, ElementName=window, Mode=OneWay}" 
                    Bitrate="{Binding SelectedPreset.Bitrate, ElementName=window, Mode=OneWay}" />

和主窗口中的一些代码来应用修改

private void EncodingQualitySlider_ValueChanged(object sender, double bitrateValue)
    {
        this.SelectedPreset?.Bitrate = bitrateValue;
    }

编辑2 :

这是一个最小的项目,再现我的问题:链接依赖属性测试项目

想要的行为是:当我启动应用程序时,我想看到preset1(比特率32)。然后如果我检查preset2,我想看到比特率值为225。

在WPF中,一个依赖属性值依赖于另一个属性值是否正确?

多亏了这个示例项目,我终于找到了如何使我的项目工作的方法。

我没有完全理解依赖属性的概念,并试图避免它,使用控件触发的事件。

我只是删除了所有从事件中设置值的代码,然后将属性绑定在模式"two - way"中。我一开始没有这样做,因为我不知道如何直接从绑定设置。为了使它工作,我使用了(就像Peter Duniho说的,谢谢!)值转换器(IValueConverter)。

谢谢大家的评论!