1

WPF のバインディングが既存のプロパティをターゲットにしていることを確認するために、静的プロパティ名プロパティを使用しています。

ここで、プロパティに関する詳細情報を静的プロパティ記述オブジェクト、Name、Type、Id などにカプセル化する必要はありませんが、名前のパスバインド可能なプロパティを 1 つと、他のすべての情報を含むプロパティを 1 つ持つ必要はありません。

問題は、WPF がプロパティの型が String ではなく PropertyInfo であると不平を言うことです。

私は何とかこの制限を回避しようとしています。たとえば、PropertyInfo を暗黙的に文字列にキャスト可能にし、ToString をオーバーライドして、PropertyInfo から文字列へ、および PropertyInfo から文字列への TypeConverter の両方を追加しようとしました。何も機能しません。

また、Name プロパティに直接バインドすることもできません。

<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;
    }
}

助言がありますか?

4

2 に答える 2

1

PropertyInfo には、PropertyInfo を文字列ではなく System.Windows.PropertyPath に変換するための TypeConverter が必要です。また、この目的で .NET の 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 に変換しても機能しません。System.Windows.PropertyPath クラスに ConverterType 属性を配置する必要があるためだと思います。ただし、所有していないクラスなので、属性を配置することはできません。XAML は TypeDescriptor インフラストラクチャを使用しないため、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>
于 2012-05-26T17:51:13.780 に答える
0

それで、あなたの構文は正しいですか?

少し前に静的クラスにバインドする必要がありました。静的にバインドするための構文は異なります。ここに文書化しました。

静的クラスのプロパティへの WPF バインディング

しかし、基本的に構文は次のとおりです。

{x:Static s:MyStaticClass.StaticValue1}
于 2012-05-26T17:07:40.760 に答える