【问题标题】:Change Source of Binding for attached property更改附加属性的绑定源
【发布时间】:2017-01-12 14:10:12
【问题描述】:

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

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

如何根据附加行为设置 ViewModel 的 A(或 B)属性值?

我不明白:

  1. 附加属性使用哪种类型? Binding? BindingBase? BindingExpressionBase? object?
  2. 我应该立即设置值吗?我要等一些活动吗?我是否应该使用另一个依赖属性来设置它的值,然后绑定到SomeProperty 并在设置DataContext 后让绑定完成这项工作?

我的失败尝试是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; }
}

【问题讨论】:

  • 只是为了澄清你的问题:你想在绑定到A 属性的依赖属性的OnPropertyChanged 回调中设置ViewModelA 属性吗?这没有任何意义。
  • @dymanoid,这就是我在这里的原因。我不确定如何使用附加属性来组织它。如果例如我会使用bool 并设置local:MyBehavior.View="True" - 然后这将导致回调运行,我可以在其中执行((ViewModel)element.DataContext).A = something 之类的操作。现在我希望在 xaml 中定义 A。因此具有约束力。还有这个没有意义的问题;)

标签: c# wpf binding attached-properties


【解决方案1】:

附加属性使用哪种类型?

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

我要立即设置值吗

您可以在注册依赖属性时为其指定默认值。引用类型(如 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...");
            }
        };
    }
}

【讨论】:

  • 这个解决方案仍然需要代码隐藏,我打算通过附加行为来消除它。想法?注意element.Loaded(另请参阅我对@dymanoid 的评论),想法是从附加行为设置值并仅使用绑定来传递源(定义哪个属性AB 应该获得该值)。
  • 还没有考虑反射,谢谢。但是绑定更灵活:我可以通过ElementName 绑定到父控件上下文,提供转换器、验证等。我的唯一问题是弄清楚View 应该是什么类型(顺便说一句,在最后的编辑版本中最好使用string) 以及如何设置源值(你现在用反射做什么)。想法? ;)
  • View 应该是什么类型取决于源属性的类型。如果源属性可以是任何类型,则 View 属性应该是一个对象。
  • 同意。如果您想在该依赖属性上调用 SetValue()(这反过来将设置绑定的源),这是正确的。但是,如果您调用 GetValue() 来获取某些东西(在您上次编辑中它是一个属性名称,在我失败的尝试中它是一个绑定为 BindingBase)并且 然后 用它做一些事情来设置一些东西怎么办? .这是否使它更清楚?您会看到,通过反射,您当前正在传递属性的 name。我想宁愿直接传递 source.. 作为绑定.. 我卡住了.. 我有绑定,但不知道如何处理它来设置它的(绑定?)源。
猜你喜欢
  • 1970-01-01
  • 2017-07-02
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-25
  • 2011-11-01
相关资源
最近更新 更多