如何使 Binding 尊重 DependencyProperty 值强制?

发布于 2024-07-13 23:34:48 字数 2166 浏览 12 评论 0原文

我有一个带有 DependencyProperty 和 CoerceValueCallback 的控件。 该属性绑定到模型对象上的属性。

当将控件属性设置为导致强制的值时,绑定会将非强制值推送到模型对象。 控件上的属性值已正确强制。

如何获取 Binding 将强制值推送到模型对象?

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;
        }
    }
}

I have a control with a DependencyProperty with a CoerceValueCallback.
This property is bound to a property on a model object.

When setting the control property to a value that causes coercion the Binding pushes the uncoerced value to the model object. The property value on the control is coerced correctly.

How do I get the Binding to push the coerced value to the model object?

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;
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

慕烟庭风 2024-07-20 23:34:48

我认为这就是强制的整个想法 - 即时正确的值而不触发任何其他依赖项的修改。 您可以使用下面的代码代替本机强制机制:

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

HTH。

I think that that's the whole idea of the coercion - correct value on the fly without triggering modification of any other dependencies. You can use the code below instead of native coercion mechanisms:

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

HTH.

梦醒灬来后我 2024-07-20 23:34:48

我不认为强制回调是双向的。 一种解决方法是在强制回调内更新模型的值。

I don't think the coerce callback is meant to be a two-way street. One workaround would be to update the model's value inside of the coerce callback.

独享拥抱 2024-07-20 23:34:48

这是扩展方法,您可以在目标对象上设置值

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);
            }
        }
    }

因此,在此方法中,使用绑定,您可以将值设置为源。 当然,源路径可以有很多名称 - 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;
    }

Here is extension method where you set value on target object

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);
            }
        }
    }

So, in this method, using binding, you can set value to source. Of cource source Path can have a lot of names - Property1.Property2.Property3 and etc.
In coerce method you need only call this method:

private static object CoerceProperty(DependencyObject d, object baseValue)
    {
        if (!Check)
        {
            var sender = (FrameworkElement)d;
            sender.SetTargetValue(MyPropertyProperty, myValue);
            return needValue;
        }
        return baseValue;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文