更改附加财产的绑定来源

时间:2017-01-12 14:10:12

标签: c# wpf binding attached-properties

如果我在附加属性中使用绑定扩展

<TextBlock local:MyBehavior.View="{Binding A}" /> <!-- or B -->

如何从该附加行为中设置ViewModel的A(或B)属性值?

我不明白:

  1. 附属物使用哪种类型? BindingBindingBaseBindingExpressionBaseobject
  2. 我应该立即设置值吗?我等一下吗?我应该使用另一个依赖属性来设置它的值,然后绑定到SomeProperty并让绑定在设置DataContext后完成工作吗?
  3. 我失败的尝试是here,为方便起见,我将其复制到下面:

    public class MyBehavior
    {
        public static BindingBase GetView(DependencyObject obj) => (BindingBase)obj.GetValue(ViewProperty);
        public static void SetView(DependencyObject obj, BindingBase value) => obj.SetValue(ViewProperty, value);
        public static readonly DependencyProperty ViewProperty =
            DependencyProperty.RegisterAttached("View", typeof(BindingBase), typeof(MyBehavior), new PropertyMetadata(null, (d, e) =>
            {
                var element = d as FrameworkElement;
                if (element == null)
                    throw new ArgumentException("Only used with FrameworkElement");
                element.Loaded += (s, a) => GetView(element); // <<
            }));
    }
    

    我不确定在标记线上做什么来设置绑定属性给出的值:

    public class ViewModel
    {
        public object A { get; set; }
        public object B { get; set; }
    }
    

1 个答案:

答案 0 :(得分:1)

  

附属物使用哪种类型?

与您在视图模型中定义源属性的类型相同,即object或附加属性应存储的任何类型的值。类型取决于您要在附加属性中存储的值的类型。

  

我应该立即设置值

注册时,可以为依赖项属性指定默认值。引用类型(如对象)的默认值通常为null

public class MyBehavior
{
    public static object GetView(DependencyObject obj) => obj.GetValue(ViewProperty);
    public static void SetView(DependencyObject obj, object value) => obj.SetValue(ViewProperty, value);

    public static readonly DependencyProperty ViewProperty =
        DependencyProperty.RegisterAttached("View", typeof(object), typeof(MyBehavior), 
            new PropertyMetadata(/*default value: */ null, new PropertyChangedCallback(OnPropertyChanged)));

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //...
    }
}

当您像任何其他依赖项属性一样绑定到视图模型时,将自动设置依赖项属性的值,例如:

<强> MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    public object A { get; set; } = "value...";
}

<强> MainWindow.xaml:

<TextBlock local:MyBehavior.View="{Binding A}" />
public class MyBehavior
{
    public static object GetView(DependencyObject obj) => obj.GetValue(ViewProperty);
    public static void SetView(DependencyObject obj, object value) => obj.SetValue(ViewProperty, value);

    public static readonly DependencyProperty ViewProperty =
        DependencyProperty.RegisterAttached("View", typeof(object), typeof(MyBehavior), 
            new PropertyMetadata(/*default value: */ null, new PropertyChangedCallback(OnPropertyChanged)));

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        object theValue = GetView(d);
        MessageBox.Show(theValue.ToString());
    }
}

如果您希望能够将视图模型属性设置为附加属性的值,则应将绑定模式设置为OneWayToSource

<TextBlock x:Name local:MyBehavior.View="{Binding A, Mode=OneWayToSource}" />

但是更新视图模型的是视图:

public MainWindow()
{
    InitializeComponent();
    DataContext = this;

    MyBehavior.SetView(txt, "new value...");
}

附加的依赖属性isself只是可以在任何DependencyObject上设置的另一个依赖属性。

修改

  

此解决方案仍然需要代码隐藏,我的意图是通过附加行为消除它。想法?注意element.Loaded(另见我对@dymanoid的评论),想法是设置附加行为的值,只使用绑定来传递源(定义哪个属性,A或B,应该得到该值)。

然后,您只需将附加属性设置为源属性的名称:

<TextBlock local:MyBehavior.View="A" />

...并使用反射在附加行为中设置此源属性的值:

public class MyBehavior
{
    public static object GetView(DependencyObject obj) => obj.GetValue(ViewProperty);
    public static void SetView(DependencyObject obj, object value) => obj.SetValue(ViewProperty, value);

    public static readonly DependencyProperty ViewProperty =
        DependencyProperty.RegisterAttached("View", typeof(object), typeof(MyBehavior), 
            new PropertyMetadata(/*default value: */ null, new PropertyChangedCallback(OnPropertyChanged)));

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var element = d as FrameworkElement;
        if (element == null)
            throw new ArgumentException("Only used with FrameworkElement");
        element.Loaded += (s, a) => 
        {
            string propertyName = GetView(element).ToString();
            if(element.DataContext != null)
            {
                System.Reflection.PropertyInfo pi = element.DataContext.GetType().GetProperty(propertyName);
                pi.SetValue(element.DataContext, "new value...");
            }
        };
    }
}