Как заставить Binding учитывать приведение значения DependencyProperty?

StackOverflow https://stackoverflow.com/questions/516210

Вопрос

У меня есть элемент управления с DependencyProperty с CoerceValueCallback.Это свойство привязано к свойству объекта модели.

При установке для свойства элемента управления значения, вызывающего принуждение, привязка отправляет непринужденный значение для объекта модели.Значение свойства элемента управления приведено правильно.

Как мне заставить привязку нажать принудительный значение для объекта модели?

void Initialize()
{
    UIObject ui = new UIObject();
    ModelObject m = new ModelObject();
    m.P = 4;

    Binding b = new Binding("P");
    b.Source = m;
    b.Mode = BindingMode.TwoWay;
    Debug.WriteLine("SetBinding");
    // setting the binding will push the model value to the UI
    ui.SetBinding(UIObject.PProperty, b);

    // Setting the UI value will result in coercion but only in the UI.
    // The value pushed to the model through the binding is not coerced.
    Debug.WriteLine("Set to -4");
    ui.P = -4;

    Debug.Assert(ui.P == 0);
    // The binding is TwoWay, the DP value is coerced to 0.
    Debug.Assert(m.P == 0); // Not true. This will be -4. Why???
}

class UIObject : FrameworkElement
{
    public static readonly DependencyProperty PProperty =
        DependencyProperty.Register("P", typeof(int), typeof(UIObject), 
        new FrameworkPropertyMetadata(
            new PropertyChangedCallback(OnPChanged), 
            new CoerceValueCallback(CoerceP)));

    public int P
    {
        get { return (int)GetValue(PProperty); }
        set { SetValue(PProperty, value); }
    }

    private static void OnPChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine(typeof(UIObject) + ".P changed from " + e.OldValue + " to " + e.NewValue);
    }

    private static object CoerceP(DependencyObject sender, object value)
    {
        int p = (int)value;
        if (p < 0)
        {
            Debug.WriteLine(typeof(UIObject) + ".P coerced from " + p + " to 0");
            p = 0;
        }
        return p;
    }
}

class ModelObject
{
    private int p;
    public int P
    {
        get
        {
            Debug.WriteLine(this + ".P returned " + this.p);
            return this.p;
        }
        set
        {
            Debug.WriteLine(this + ".P changed from +" + this.p + " to " + value);
            this.p = value;
        }
    }
}
Это было полезно?

Решение

Я не думаю, что обратный вызов по принуждению должен быть улицей с двусторонним движением.Одним из обходных путей было бы обновление значения модели внутри обратного вызова принуждения.

Другие советы

Я думаю, что в этом вся идея приведения - корректировать значение на лету, не вызывая модификации каких-либо других зависимостей.Вы можете использовать приведенный ниже код вместо собственных механизмов приведения:

OnPChanged(/* ... */)
{
    // ...
    var coercedP = CoerceP(P);
    if (P != coercedP)
        P = coercedP;
    // ...
}

ХТХ.

Вот метод расширения, в котором вы устанавливаете значение для целевого объекта

public static void SetTargetValue<T>(this FrameworkElement element, DependencyProperty dp, T value)
    {
        var binding = BindingOperations.GetBinding(element, dp);
        if (binding == null) return;
        var name = binding.Path.Path;
        var splits = name.Split('.');
        var target = element.DataContext;
        for (var i = 0; i < splits.Length; i++)
        {
            PropertyInfo property;
            if (i == splits.Length - 1)
            {
                property = target.GetType().GetProperty(splits[i]);
                property.SetValue(target, value);
            }
            else
            {
                property = target.GetType().GetProperty(splits[i]);
                target = property.GetValue(target);
            }
        }
    }

Итак, в этом методе, используя привязку, вы можете установить значение source.Конечно, исходный путь может иметь множество имен — Property1.Property2.Property3 и т. д.В методе принуждения вам нужно вызвать только этот метод:

private static object CoerceProperty(DependencyObject d, object baseValue)
    {
        if (!Check)
        {
            var sender = (FrameworkElement)d;
            sender.SetTargetValue(MyPropertyProperty, myValue);
            return needValue;
        }
        return baseValue;
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top