【问题标题】:How do I bind the Binding.Path property to underlying data?如何将 Binding.Path 属性绑定到基础数据?
【发布时间】:2009-04-22 16:04:16
【问题描述】:

我正在尝试以一种非常动态的方式绑定 TextBlock 的 Text 属性。我需要从底层对象获取路径。

这是数据模板:

<DataTemplate DataType={x:Type local:DummyClass}>
  <TextBlock Text={Binding Path=???} />
</DataTemplate>

DummyClass 对象有一个名为“FieldValuePath”的属性 - 需要将路径放在 ???是。

这背后的想法是数据模板应该是一个用于查看/编辑任何对象的任何属性的 GUI。因此,最好能够声明 XAML,它将一些控件(文本框、文本块、日期选择器等)绑定到给定的属性。

也许有人对如何实现这样的事情有任何建议?

【问题讨论】:

    标签: .net wpf data-binding


    【解决方案1】:

    如果您在后面的代码中创建绑定,那么您可以让它工作。例如一个简单的代码生成绑定是:

    Binding binding = new Binding("BindingPath");
    binding.Mode = BindingMode.TwoWay;
    BindingOperations.SetBinding(textBoxName, TextBox.TextProperty, binding);
    

    由于此绑定中的路径(“BindingPath”)只是一个字符串,该字符串可以来自任何可用的对象。

    不过,您需要挂钩数据项的创建以设置这些绑定。


    基于您的 cmets 的进一步可能性:

    This blog post 概述了一种通过从 MarkupExtension 继承来创建自定义绑定类的方法。您可以以此为起点,将我的建议包装成可重复使用的 xaml 标记,以用于您的特殊绑定案例。


    更多想法:

    好的,这是一个有趣的问题,所以我决定花一点时间看看我是否能想出一个可行的解决方案。对于以下代码示例的长度,我提前道歉...

    基于我上面链接的博客文章的解决方案,我创建了这个类:

    public class IndirectBinder : MarkupExtension
        {
            public string IndirectProperty { get; set; }
    
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                //try to get bound items for our custom work
                DependencyObject targetObject;
                DependencyProperty targetProperty;
                bool status = TryGetTargetItems(serviceProvider, out targetObject, out targetProperty);
    
                if (status)
                {
                    Control targetControl = targetObject as Control;
                    if (targetControl == null) return null;
    
                    //Find the object to take the binding from
                    object dataContext = targetControl.DataContext;
                    if (dataContext == null) return null;
    
                    //Reflect out the indirect property and get the value
                    PropertyInfo pi = dataContext.GetType().GetProperty(IndirectProperty);
                    if (pi == null) return null;
    
                    string realProperty = pi.GetValue(dataContext, null) as string;
                    if (realProperty == null) return null;
    
                    //Create the binding against the inner property
                    Binding binding = new Binding(realProperty);
                    binding.Mode = BindingMode.TwoWay;
                    BindingOperations.SetBinding(targetObject, targetProperty, binding);
    
                    //Return the initial value of the binding
                    PropertyInfo realPi = dataContext.GetType().GetProperty(realProperty);
                    if (realPi == null) return null;
    
                    return realPi.GetValue(dataContext, null);
    
                }
    
                return null;
    
            }
    
            protected virtual bool TryGetTargetItems(IServiceProvider provider, out DependencyObject target, out DependencyProperty dp)
            {
                target = null;
                dp = null;
                if (provider == null) return false;
    
                //create a binding and assign it to the target
                IProvideValueTarget service = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));
                if (service == null) return false;
    
                //we need dependency objects / properties
                target = service.TargetObject as DependencyObject;
                dp = service.TargetProperty as DependencyProperty;
                return target != null && dp != null;
            }
    

    您可以将此新标记与以下 xaml 一起使用:

    <TextBox Text="{local:IndirectBinder IndirectProperty=FieldValuePath}"/>
    

    其中 TextBox 可以是任何从控件继承的类,Text 可以是任何依赖属性。

    显然,如果您需要公开任何其他数据绑定选项(例如单向或双向绑定),那么您需要向类添加更多属性。

    虽然这是一个复杂的解决方案,但它比使用转换器的一个优点是最终创建的绑定是针对实际的内部属性而不是对象。这意味着它可以正确响应 PropertyChanged 事件。

    【讨论】:

    • 好吧,这可能是一种可行的方法,但我真的很希望能够在 XAML 中创建绑定,因为我打算根据状态编写具有不同的绑定属性基础数据。但也许我确实可以尝试在代码隐藏中编写所有这些逻辑......
    • 此外,我计划为其指定绑定的不仅仅是 TextBlock。它应该是任何对象上任何属性的编辑器/查看器。我想我应该添加到顶帖。
    • 感谢您的回答,并感谢您的链接。我要试试这个
    • +1。非常有帮助的sn-p。我调整了返回值以支持周期分隔的路径。除此之外,它非常适合我的需求。
    【解决方案2】:

    我建议使用转换器:

     <DataTemplate DataType={x:Type local:DummyClass}>
        <TextBlock Text={Binding Converter={StaticResource PropertyNameToValueConverter, ConverterParameter=FieldValuePath}} />
     </DataTemplate>
    

    转换器将获取类和属性名称,并从那里使用反射返回值。

    【讨论】:

    • 但它只会返回一次。
    【解决方案3】:
    <DataTemplate DataType={x:Type local:DummyClass}>
      <TextBlock Text={Binding Path=FieldValuePath} />
    </DataTemplate>
    

    应该是正确的方法。如果您正在侦听 FieldValuePath 中的更改,则需要确保 DummyClass 继承自 INotifyPropertyChanged,并且在 FieldValuePath 更改时触发属性更改事件。

    【讨论】:

    • 抱歉,但这会将文本设置为等于 FieldValuePath 值。所以如果你有 dummyObject.FieldValuePath=="Property1" 你会得到 TextBlock.Text== "Property1"
    • 在这种情况下,您的所有属性是否都声明为 DependencyProperties?如果是这种情况,您应该能够绑定到属性 itsel。这可能会有所帮助...geekswithblogs.net/thibbard/archive/2008/04/22/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 2013-10-04
    相关资源
    最近更新 更多