Set the binding value directly

你离开我真会死。 提交于 2019-12-30 04:40:13

问题


Is it possible to set the value behind a two-way binding directly, without knowing the bound property?

I have an attached property that is bound to a property like this:

<Element my:Utils.MyProperty="{Binding Something}" />

Now I want to change the value that is effectively stored in Something from the perspective of the attached property. So I cannot access the bound property directly, but only have references to the DependencyObject (i.e. the Element instance) and the DependencyProperty object itself.

The problem when simply setting it via DependencyObject.SetValue is that this effectively removes the binding, but I want to change the underlying bound property.

Using BindingOperations I can get both the Binding and the BindingExpression. Now is there a way to access the property behind it and change its value?


回答1:


Okay, I have solved this now myself using a few reflection tricks on the binding expression.

I basically look at the binding path and the responding data item and try to resolve the binding myself. This will probably fail for more complex binding paths but for a simple property name as in my example above, this should work fine.

BindingExpression bindingExpression = BindingOperations.GetBindingExpression(dependencyObj, dependencyProperty);
if (bindingExpression != null)
{
    PropertyInfo property = bindingExpression.DataItem.GetType().GetProperty(bindingExpression.ParentBinding.Path.Path);
    if (property != null)
        property.SetValue(bindingExpression.DataItem, newValue, null);
}



回答2:


Try setting a default value in the PropertyMetadata

You can find more information on MSDN - http://msdn.microsoft.com/en-us/library/system.windows.propertymetadata.aspx

Here is an example :

  public Boolean State
  {
    get { return (Boolean)this.GetValue(StateProperty); }
    set { this.SetValue(StateProperty, value); } 
  }
  public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
    "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(myDefaultValue));



回答3:


The problem when simply setting it via DependencyObject.SetValue is that this effectively removes the binding, but I want to change the underlying bound property.

This is true if the Binding.Mode is set to OneWay. If it is set to TwoWay, using DependencyObject.SetValue won't remove its binding.

This is a quote from Pro WPF 4.5 in C# (page 232):

Removing a binding: If you want to remove a binding so that you can set a property in the usual way, you need the help of the ClearBinding() or ClearAllBindings() method. It isn’t enough to simply apply a new value to the property. If you’re using a two-way binding, the value you set is propagated to the linked object, and both properties remain synchronized.

So, to be able to change (and propagate) the my:Utils.MyProperty with SetValue without removing its binding:

<Element my:Utils.MyProperty="{Binding Something, Mode=TwoWay}" />



回答4:


You can pass value via a binding on a dummy object.

    public static void SetValue(BindingExpression exp, object value)
    {
        if (exp == null)
            throw new ValueCannotBeNullException(() => exp);
        Binding dummyBinding = new Binding(exp.ParentBinding.Path.Path);
        dummyBinding.Mode = BindingMode.OneWayToSource;
        dummyBinding.Source = exp.DataItem;
        SetValue(dummyBinding, value);
    }

    public static void SetValue(Binding binding, object value)
    {
        BindingDummyObject o = new BindingDummyObject();
        BindingOperations.SetBinding(o, BindingDummyObject.ValueProperty, binding);
        o.Value = value;
        BindingOperations.ClearBinding(o, BindingDummyObject.ValueProperty);
    }

This is my dummy object

   internal class BindingDummyObject : DependencyObject
{
    public object Value
    {
        get
        {
            return (object)GetValue(ValueProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(BindingDummyObject));
}



回答5:


I had a similar issue when trying to implement a menu that was backed by an enumeration. I wanted to be able to set the underlying property (which was an enum) to the value associated with the menu item.

In my example, I attached two properties to an MenuItem:

    public static readonly DependencyProperty EnumTargetProperty = DependencyProperty.RegisterAttached(
        "EnumTarget",
        typeof(object),
        typeof(MenuItem),
        new PropertyMetadata(null, EnumTargetChangedCallback)
        );

    public static readonly DependencyProperty EnumValueProperty = DependencyProperty.RegisterAttached(
        "EnumValue",
        typeof(object),
        typeof(MenuItem),
        new PropertyMetadata(null, EnumValueChangedCallback)
        );

And the markup looks like this:

                <MenuItem.ItemContainerStyle>
                    <Style TargetType="MenuItem">
                        <Setter Property="IsCheckable" Value="True"/>
                        <Setter Property="local:EnumMenuItem.EnumValue" Value="{Binding EnumMember}"/>
                        <Setter Property="local:EnumMenuItem.EnumTarget" Value="{Binding RelativeSource={RelativeSource AncestorType=local:MainWindow}, Path=DataContext.Settings.AutoUpdateModel.Ring}"/>
                        <Setter Property="Header" Value="{Binding DisplayName}"/>
                        <Setter Property="ToolTip" Value="{Binding ToolTip}"/>
                    </Style>
                </MenuItem.ItemContainerStyle>

The item source for the parent menu item was bound to an MarkupExtension implementation that provided values for each member in the enum.

Now, when the menu item was checked, I used this code to set the value of the property without removing the binding.

        menuItem.Checked += (sender, args) =>
        {
            var checkedMenuItem = (MenuItem)sender;
            var targetEnum = checkedMenuItem.GetValue(EnumTargetProperty);
            var menuItemValue = checkedMenuItem.GetValue(EnumValueProperty);
            if (targetEnum != null && menuItemValue != null)
            {
                var bindingExpression = BindingOperations.GetBindingExpression(d, EnumTargetProperty);
                if (bindingExpression != null)
                {
                    var enumTargetObject = bindingExpression.ResolvedSource;
                    if (enumTargetObject != null)
                    {
                        var propertyName = bindingExpression.ResolvedSourcePropertyName;
                        if (!string.IsNullOrEmpty(propertyName))
                        {
                            var propInfo = enumTargetObject.GetType().GetProperty(propertyName);
                            if (propInfo != null)
                            {
                                propInfo.SetValue(enumTargetObject, menuItemValue);
                            }
                        }
                    }
                }
            }
        };

This seems to work fine for my scenario with a complex path.

I hope this helps out!



来源:https://stackoverflow.com/questions/11121057/set-the-binding-value-directly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!