如何创建静态可绑定属性描述对象

时间:2012-05-26 16:43:27

标签: c# wpf properties propertyinfo

为了确保WPF中的绑定是针对现有属性,我使用静态属性名称属性。

现在我不想将有关我的属性的更多信息封装到静态属性描述对象,Name,Type,Id等中,但不必为名称和一个具有所有其他信息的属性创建一个path-bindable属性。

问题是WPF抱怨属性类型错误,而不是String而是PropertyInfo。

我试图以某种方式解决这个限制。例如,我尝试将我的PropertyInfo隐式地转换为字符串,重写ToString并将PropertyInfo中的TypeConverter添加到字符串和PropertyInfo中的字符串。什么都行不通。

我也无法直接绑定到Name-property。

<TextBlock Text="{Binding Path={x:Static l:Test.TitleProperty}}" />

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        TypeDescriptor.AddAttributes(typeof(string),
          new TypeConverterAttribute(typeof(StringFromPropertyConverter)));

        DataContext = new Test { Title = "hello" };
    }
}

public class Test
{
    public static readonly PropertyInfo TitleProperty = 
      new PropertyInfo { Name = "Title" };

    public string Title { get; set; }
}

[TypeConverter(typeof(PropertyToStringConverter))]
public class PropertyInfo
{
    public string Name { get; set; }

    public static implicit operator string(PropertyInfo p) { return p.Name; }

    public override string ToString()
    {
        return Name;
    }
}

public class PropertyToStringConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context,
      Type destinationType)
    {
        if (destinationType == typeof(string)) return true;
        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
      System.Globalization.CultureInfo culture, object value,
      Type destinationType)
    {
        return ((PropertyInfo)value).Name;
    }
}

public class StringFromPropertyConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context,
      Type sourceType)
    {
        if (sourceType == typeof(PropertyInfo)) return true;
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
      System.Globalization.CultureInfo culture, object value)
    {
        return ((PropertyInfo)value).Name;
    }
}

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

您的PropertyInfo需要使用TypeConverter将PropertyInfo转换为System.Windows.PropertyPath,而不是字符串。此外,您可能需要考虑重用.NETs System.Reflection.PropertyInfo来实现此目的。

我以前见过这种方法,其主要原因是避免在属性变更通知中使用“魔法字符串”。所以你也可以看看如何使用System.Linq.Expressions来获得PropertyInfo:

public static class ReflectionHelper
{
    public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> getter)
    {
        return (PropertyInfo)((MemberExpression)getter.Body).Member;
    }
}

并像这样使用它:

public class Test
{
    public static readonly PropertyInfo TitleProperty = ReflectionHelper.GetPropertyInfo<Test>(x => x.Title);

    public string Title { get; set; }
}

编辑:新答案

是的,你是对的。即使您定义TypeConverter以将PropertyInfo转换为System.Windows.PropertyPath,它也不起作用。我认为这是因为ConverterType属性应该放在System.Windows.PropertyPath类上。但由于它是一个你不拥有的类,你不能在其上放置属性。使用TypeDescriptor添加属性将不起作用,因为XAML不使用TypeDescriptor基础结构。

您可以使用MarkupExtension完成转换。这是一个完整的代码(它使用System.Reflection命名空间中的PropertyInfo):

<强> ReflectionHelper.cs

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace WpfApplication
{
    public static class ReflectionHelper
    {
        public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> getter)
        {
            return (PropertyInfo)((MemberExpression)getter.Body).Member;
        }
    }
}

<强> test.cs中

using System.Reflection;

namespace WpfApplication
{
    public class Test
    {
        public static readonly PropertyInfo TitleProperty = ReflectionHelper.GetPropertyInfo<Test>(x => x.Title);

        public string Title { get; set; }
    }
}

<强> PropertyInfoPathExtension.cs

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;

namespace WpfApplication
{
    public class PropertyInfoPathExtension : MarkupExtension
    {
        private readonly PropertyInfo propertyInfo;

        public PropertyInfoPathExtension(PropertyInfo propertyInfo)
        {
            if (propertyInfo == null)
                throw new ArgumentNullException("propertyInfo");

            this.propertyInfo = propertyInfo;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return new PropertyPath(propertyInfo);
        }
    }
}

<强> MainWindow.xaml

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:Test Title="hello"/>
    </Window.DataContext>
    <TextBlock Text="{Binding Path={local:PropertyInfoPath {x:Static local:Test.TitleProperty}}}"/>
</Window>

答案 1 :(得分:0)

那么,你的语法是否正确?

我不得不在不久前绑定到静态类。绑定到静态的语法是不同的。我在这里记录了它。

WPF Binding to a property of a static class

但基本上语法是:

{x:Static s:MyStaticClass.StaticValue1}