拥有依赖于WPF中另一个属性值的依赖项属性值是否正确?

时间:2015-10-15 17:12:41

标签: c# wpf

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

public class Settings
{
    EnumType Mode;
    float Rate;
}

属性Rate的值取决于属性Mode的值。 我使用CoerceValue函数进行更新,确保Rate值始终正确。

该属性以只读模式(单向)绑定到UI,因为我想在写入时执行一些额外的过程。 所以我在UI控件上创建了一个事件,以了解Rate属性何时更改。

在我的窗口中,UI绑定到静态变量SelectedSettings

我的问题如下:

当我更改SelectedSettings的值(使用其他设置类)时,它不会在UI中加载新设置,而是执行以下操作:

  • 在UI中设置新的Mode
  • 上一个操作启动coerceValue过程并修改Rate值。
  • 费率值的修改触发了事件。
  • 触发事件在SelectedSettings
  • 中写入新的rate值
  • 设置新的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

这是一个重现我的问题的最小项目: Linked dependency property test project

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

非常感谢。

1 个答案:

答案 0 :(得分:0)

由于示例项目,我终于找到了如何让我的项目工作。

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

我只是从事件中删除了设置我的设置值的所有代码,然后将属性绑定放在模式&#34; TwoWay&#34;中。我首先没有这样做,因为我不知道如何直接从绑定设置我的设置。为了使它工作,我使用(像Peter Duniho说,谢谢!)值转换器(IValueConverter)。

谢谢大家的意见!