【问题标题】:Set the binding value directly直接设置绑定值
【发布时间】:2012-06-22 16:16:39
【问题描述】:

是否可以在不知道绑定属性的情况下直接设置双向绑定后面的值?

我有一个附加属性,它绑定到这样的属性:

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

现在我想从附加属性的角度更改有效存储在Something 中的值。所以我不能直接访问绑定的属性,而只能引用DependencyObject(即Element实例)和DependencyProperty对象本身。

通过DependencyObject.SetValue 简单地设置它的问题是这有效地删除了绑定,但我想更改底层绑定属性。

使用BindingOperations 我可以获得BindingBindingExpression。现在有没有办法访问它背后的属性并改变它的值?

【问题讨论】:

  • 您是否自己编写了附加道具,因为您可以更改其 PropertyMetadata 吗?我猜不是?
  • @HellScream 是的,我正在自己编写属性,所以如果有帮助,我可以更改元数据。
  • DependencyObject.SetValue 真的会移除绑定吗?如果是这样,绑定替换为什么?

标签: wpf binding attached-properties


【解决方案1】:

好的,我现在自己在绑定表达式上使用了一些反射技巧解决了这个问题。

我主要看绑定路径和响应数据项,并尝试自己解决绑定。对于更复杂的绑定路径,这可能会失败,但对于我上面示例中的简单属性名称,这应该可以正常工作。

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

【讨论】:

  • 路径复杂时不起作用。因为当路径类似于 Customer.Name 时,BindingExpression 的 DataItem 中不存在实际属性。它归其他对象所有(在这种情况下归客户所有)。
  • @KubilayKara 如果您阅读我的回答,您会注意到我说它只适用于简单的属性名称。如果您有复杂的绑定路径,则必须先解析路径并遍历对象以找到正确的属性。
【解决方案2】:

尝试在PropertyMetadata中设置一个默认值

您可以在 MSDN 上找到更多信息 - http://msdn.microsoft.com/en-us/library/system.windows.propertymetadata.aspx

这是一个例子:

  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】:

简单地通过 DependencyObject.SetValue 设置时的问题是 这有效地消除了绑定,但我想改变 底层绑定属性。

如果 Binding.Mode 设置为 OneWay,则为 true。如果设置为 TwoWay,则使用 DependencyObject.SetValue 不会删除其绑定。

这是来自 Pro WPF 4.5 在 C# 中的引用(第 232 页):

删除绑定:如果您想删除绑定以便可以 以通常的方式设置属性,您需要 ClearBinding() 或 ClearAllBindings() 方法。仅仅简单是不够的 对属性应用新值。如果您使用双向绑定, 您设置的值将传播到链接对象,并且两者 属性保持同步。

因此,为了能够使用 SetValue 更改(和传播)my:Utils.MyProperty 而无需删除其绑定:

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

【讨论】:

  • SetValue() 为我移除绑定,即使我处于双向模式。
【解决方案4】:

您可以通过虚拟对象上的绑定来传递值。

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

这是我的虚拟对象

   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】:

    在尝试实现由枚举支持的菜单时,我遇到了类似的问题。我希望能够将基础属性(它是一个枚举)设置为与菜单项关联的值。

    在我的示例中,我将两个属性附加到一个 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)
            );
    

    标记看起来像这样:

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

    父菜单项的项目源绑定到为枚举中的每个成员提供值的 MarkupExtension 实现。

    现在,当检查菜单项时,我使用此代码设置属性的值而不删除绑定。

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

    这似乎适用于我的路径复杂的场景。

    我希望这会有所帮助!

    【讨论】:

      【解决方案6】:

      这是我为TextBlock 所做的:

      BindingExpression bindingExpression = textBlock.GetBindingExpression(TextBlock.TextProperty);
      string propertyName = bindingExpression.ResolvedSourcePropertyName;
      PropertyInfo propertyInfo;
      Type type = bindingExpression.DataItem.GetType();
      object[] indices = null;
      if (propertyName.StartsWith("[") && propertyName.EndsWith("]"))
      {
          // Indexed property
          propertyInfo = type.GetProperty("Item");
          indices = new object[] { propertyName.Trim('[', ']') };
      } 
      else
          propertyInfo = type.GetProperty(propertyName);
      if (propertyInfo.PropertyType == typeof(string))
      {
          propertyInfo.SetValue(bindingExpression.DataItem, text, indices);
          // To update the UI.
          bindingExpression.UpdateTarget();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-30
        • 2018-01-03
        • 2017-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-28
        相关资源
        最近更新 更多