问题
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