何时应用依赖项 属性?

When dependency property is being applied?

我现在正在做一些培训项目。它应该将数字转换成不同的字符串。 这是转换后的控件,我在 Main Window 中以最底层的方式使用它。 所以第一个问题是我想根据传递给 OutputFormatProperty 的值创建转换器的实例,所以在这种情况下,我创建的转换器应该是 OctalConverter 类型,但我得到的是默认类型,这是为什么呢? 另一件事是,我不想通过将转换器中的 InputValue 绑定到 CurrentValue 来更改它,它与 NotifyPropertyChanged 一起使用,但它似乎不能那样工作。

public partial class ConverterDisplay : UserControl {

    private const int DEFAULT_INPUT_VALUE = 0;
    private readonly ObservableCollection <DisplayField> _displayFields;
    private AbstractNumberConverter _converter;

    public static readonly DependencyProperty InputValueProperty = DependencyProperty.Register (
        "InputValue",
        typeof(int),
        typeof(ConverterDisplay),
        new PropertyMetadata (DEFAULT_INPUT_VALUE));

    public static readonly DependencyProperty OutputFormatProperty = DependencyProperty.Register (
        "OutputFormat",
        typeof(NumberSystems),
        typeof(ConverterDisplay),
        new FrameworkPropertyMetadata (NumberSystems.Binary));


    public int InputValue {
        get {
            return (int) GetValue (InputValueProperty);
        }
        set {
            SetValue (InputValueProperty, value);
            UpdateDisplay ();
        }
    }
    public NumberSystems OutputFormat {
        get {
            return (NumberSystems) GetValue (OutputFormatProperty); 

        }
        set {
            SetValue (OutputFormatProperty, value);
        }
    }

    public ObservableCollection <DisplayField> DisplayFields {
        get { return _displayFields; }
    }

    public ConverterDisplay () {
        _displayFields = new ObservableCollection<DisplayField> ();
        InitializeComponent ();
        CreateConverter ();
    }

    private void UpdateDisplay () {
        var convertedNumberString = _converter.GetString (InputValue);

        if (_displayFields.Count > convertedNumberString.Length)
            ResetDisplayFields ();

        while (_displayFields.Count < convertedNumberString.Length)
            AddDisplayField ();

        UpdateValues (convertedNumberString);
    }

    private void UpdateValues (string convertedString) {
        if (_displayFields.Count == 0) return;

        for (int i = 0; i < _displayFields.Count; i++) {
            _displayFields [i].NumberValue = convertedString [i];
        }
    }

    private void AddDisplayField () {
        _displayFields.Insert (
            0,
            new DisplayField ((int)OutputFormat, _displayFields.Count));
    }

    private void ResetDisplayFields () {
        _displayFields.Clear ();
    }

    private void CreateConverter () {
        switch (OutputFormat) {
            case NumberSystems.Binary:
                _converter = new BinaryConverter ();
                break;
            case NumberSystems.Octal:
                _converter = new OctalConverter ();
                break;
            case NumberSystems.Hexadecimal:
                _converter = new HexadecimalConverter ();
                break;
        }
    }
}

public enum NumberSystems {
    Binary = 2,
    Octal = 8,
    Hexadecimal = 16
}

然后在 Main Window 我正在尝试使用该控件

 <converters:ConverterDisplay x:Name="octConverter"
                              InputValue="{Binding ElementName=Window,Path=CurrentValue}"
                              OutputFormat="Octal"/>

以防万一

public int CurrentValue {
        get { return _currentValue; }
        set {
            if (value == _currentValue)
                return;
            ValidateNewValue (value);
            OnPropertyChanged ();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName));
    }

===========================

编辑 #1

我不太喜欢那个解决方案,但我在 ConverterDisplay 中创建了 public 方法来创建转换器,它在 MainWindow 初始化后被调用,所以现在转换器是正确的。 另一件事是如何将我的 UpdateDisplay 方法绑定到 InputValueProperty?我通过验证发现它获得了正确的值,但我看不出如何在不创建静态内容的情况下 运行 该方法。

关于你的第二个问题(将 UpdateDisplay 方法绑定到 InputValueProperty:一般来说,在依赖 属性 的 [=21] 中调用任何方法都不是最好的主意=],因为在使用数据绑定填充依赖项 属性 的值时永远不会调用此 setter,正如所指出的 at MSDN:

The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers. When you implement custom dependency properties, you must account for this behavior and should avoid placing any other code in your property wrapper other than the property system methods GetValue and SetValue.

相反,创建一个每当 InputValue 的内容发生变化时调用的回调方法,并从那里调用 UpdateDisplay

public static readonly DependencyProperty InputValueProperty = DependencyProperty.Register (
    "InputValue",
    typeof(int),
    typeof(ConverterDisplay),
    new PropertyMetadata (DEFAULT_INPUT_VALUE, InputValueChangedCallback));


private static void InputValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
    var userControl = dependencyObject as ConverterDisplay;
    if (userControl != null)
        userControl.UpdateDisplay();
}