如何将Binding.Path属性绑定到基础数据?

时间:2009-04-22 16:04:16

标签: .net wpf data-binding

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

这是DataTemplate:

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

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

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

也许有人对如何实施此类事情有任何建议?

3 个答案:

答案 0 :(得分:7)

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

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

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

您需要隐藏数据项的创建以设置这些绑定。


根据您的意见提供进一步的可能性:

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事件作出反应。

答案 1 :(得分:2)

我建议使用转换器:

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

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

答案 2 :(得分:0)

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

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