Вопрос по wpf, binding, attached-properties – Установите значение привязки напрямую

5

Можно ли установить значение двухсторонней привязки напрямую, не зная связанного свойства?

У меня есть прикрепленное свойство, которое связано с таким свойством:

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

Теперь я хочу изменить значение, которое эффективно хранится вSomething с точки зрения прикрепленной собственности. Поэтому я не могу получить доступ к связанному свойству напрямую, а имею только ссылки наDependencyObject (т.е. экземпляр элемента) иDependencyProperty сам объект.

Проблема при простой настройке черезDependencyObject.SetValue является то, что это эффективно удаляет привязку, но я хочу изменить базовое свойство привязки.

С помощьюBindingOperations Я могу получить какBinding иBindingExpression, Теперь есть ли способ получить доступ к свойству за ним и изменить его стоимость?

@ HellScream Да, я пишу свойство самостоятельно, поэтому я могу изменить метаданные, если это поможет. poke
Вы написали прикрепленную подпорку самостоятельно, как вы можете изменить ее PropertyMetadata? Я думаю, нет? Tom Kerkhove

Ваш Ответ

5   ответов
0

Попробуйте установить значение по умолчанию в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));
Error: User Rate Limit ExceededafterwardsError: User Rate Limit Exceeded poke
0

У меня была похожая проблема при попытке реализовать меню, которое было поддержано перечислением. Я хотел иметь возможность установить базовое свойство (которое было перечислением) на значение, связанное с пунктом меню.

В моем примере я прикрепил два свойства к 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);
                            }
                        }
                    }
                }
            }
        };

Кажется, это хорошо работает для моего сценария со сложным путем.

Я надеюсь, что это поможет!

0

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.

Это верно, если Binding.Mode установлен в OneWay. Если он установлен на TwoWay, использование DependencyObject.SetValue не удалит его привязку.

Это цитата из Pro WPF 4.5 в C # (стр. 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.

Итак, чтобы иметь возможность изменять (и распространять) my: Utils.MyProperty с SetValue, не удаляя его привязку:

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

Вы можете передать значение через привязку к фиктивному объекту.

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

Хорошо, теперь я решил это сам, используя несколько трюков для отражения выражения связывания.

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

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);
}
Error: User Rate Limit Exceeded poke
Error: User Rate Limit Exceeded

Похожие вопросы