2

我试图使用[TypeDescriptionProviderAttribute]为了给我的类定制一个类型描述符。这工作,但是当我实现INotifyPropertyChanged WPF似乎忽略了自定义类型描述符,并直接为CLR属性(如果存在)。以下是一段代码,稍后我将粘贴完整示例:当INotifyPropertyChanged被实现时,为什么WPF似乎绕过了TypeDescriptionProviderAttribute?

//[TypeDescriptionProvider(typeof(MyProvider))] 
class MyModel : Object 
    //, INotifyPropertyChanged 
    //, ICustomTypeDescriptor 
{ 
    public string TheProperty { get { return "CLR - TheProperty"; } } 

我将TextBlock绑定到TheProperty。当我...

  • 让一切评论

    我看到的 - 如预期 “CLR利人”。

  • 使用[TypeDescriptionProvider]

    我看到的 - 如预期 “MyPropertyDescriptor利人”。

  • 使用ICustomTypeDescriptor

    我看到的 - 如预期 “MyPropertyDescriptor利人”。

  • 使用ICustomTypeDescriptorINotifyPropertyChanged

    我看到的 - 如预期 “MyPropertyDescriptor利人”。

  • 使用[TypeDescriptionProvider]INotifyPropertyChanged

    我看到 “CLR - 利人”。 这是为什么?奇怪的是,自定义属性没有 CLR属性正常显示。我的自定义类型描述符还会返回一个“MyPropertyDescriptor - AnotherProperty”,它可以在所有情况下都起作用,因为没有定义CLR AnotherProperty

综上所述,鉴于此XAML预期,因为该模型没有一个名为 “AnotherProperty” 一个CLR属性

<StackPanel> 
    <TextBlock Text="{Binding TheProperty}" /> 
    <TextBlock Text="{Binding AnotherProperty}" /> 
</StackPanel> 

AnotherProperty始终有效。 TheProperty按预期工作除了[TypeDescriptionProvider]INotifyPropertyChanged都使用。

以下是完整的代码。这是一个有点长,但大部分是不相关的,它只是由System.ComponentModel

public partial class TestWindow : Window 
{ 
    public TestWindow() 
    { 
     InitializeComponent(); 
     DataContext = new MyModel(); 
    } 
} 

//[TypeDescriptionProvider(typeof(MyProvider))] 
class MyModel : Object 
    //, INotifyPropertyChanged 
    //, ICustomTypeDescriptor 
{ 
    public string TheProperty { get { return "CLR - TheProperty"; } } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public AttributeCollection GetAttributes() 
    { 
     return TypeDescriptor.GetAttributes(this); 
    } 

    public string GetClassName() 
    { 
     return TypeDescriptor.GetClassName(this); 
    } 

    public string GetComponentName() 
    { 
     return TypeDescriptor.GetComponentName(this); 
    } 

    public TypeConverter GetConverter() 
    { 
     return TypeDescriptor.GetConverter(this); 
    } 

    public EventDescriptor GetDefaultEvent() 
    { 
     return TypeDescriptor.GetDefaultEvent(this); 
    } 

    public PropertyDescriptor GetDefaultProperty() 
    { 
     return TypeDescriptor.GetDefaultProperty(this); 
    } 

    public object GetEditor(Type editorBaseType) 
    { 
     return TypeDescriptor.GetEditor(this, editorBaseType); 
    } 

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    { 
     return TypeDescriptor.GetEvents(this, attributes); 
    } 

    public EventDescriptorCollection GetEvents() 
    { 
     return TypeDescriptor.GetEvents(this); 
    } 

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     return TypeDescriptor.GetProperties(this, attributes); 
    } 

    public PropertyDescriptorCollection GetProperties() 
    { 
     return MyTypeDescriptor.GetCustomProperties(); 
    } 

    public object GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return this; 
    } 
} 


class MyProvider : TypeDescriptionProvider 
{ 
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) 
    { 
     return new MyTypeDescriptor(); 
    } 
} 


class MyTypeDescriptor : CustomTypeDescriptor 
{ 
    public override PropertyDescriptorCollection GetProperties() 
    { 
     return GetCustomProperties(); 
    } 

    public static PropertyDescriptorCollection GetCustomProperties() 
    { 
     return new PropertyDescriptorCollection(
      new[] { 
       new MyPropertyDescriptor("TheProperty"), 
       new MyPropertyDescriptor("AnotherProperty") 
      }); 
    } 
} 


class MyPropertyDescriptor : PropertyDescriptor 
{ 
    public MyPropertyDescriptor(string propName) 
     : base(propName, null) 
    { 
    } 

    public override bool CanResetValue(object component) 
    { 
     return false; 
    } 

    public override Type ComponentType 
    { 
     get { return typeof(MyModel); } 
    } 

    public override object GetValue(object component) 
    { 
     return "MyPropertyDescriptor - " + Name; 
    } 

    public override bool IsReadOnly 
    { 
     get { return true; } 
    } 

    public override Type PropertyType 
    { 
     get { return typeof(string); } 
    } 

    public override void ResetValue(object component) 
    { 
     throw new InvalidOperationException("cannot reset value"); 
    } 

    public override void SetValue(object component, object value) 
    { 
     throw new InvalidOperationException("property is readonly"); 
    } 

    public override bool ShouldSerializeValue(object component) 
    { 
     return true; 
    } 
} 
+0

当我试图找出问题背后的原因(以及解决方案以及......)时,我很想知道您究竟在这里实现了什么目标?动态属性? –

+0

@AngelWPF - 用几句话来描述并不容易,但是当我在一两天内写出描述时,我会发布一个链接。 –

回答

7

老问题必需的,但为人们寻找答案..

问题是System.Windows.PropertyPath。 ResolvePropertyName(字符串,对象,类型,对象,布尔)私有方法。我在.NET 4.0的PresentationFramework.dll中找到了它。

从中提取。NET的反射:

object propertyHelper = DependencyProperty.FromName(str, ownerType); 
if ((propertyHelper == null) && (item is ICustomTypeDescriptor)) 
{ 
    propertyHelper = TypeDescriptor.GetProperties(item)[str]; 
} 
if ((propertyHelper == null) && ((item is INotifyPropertyChanged) || (item is DependencyObject))) 
{ 
    propertyHelper = this.GetPropertyHelper(ownerType, str); 
} 
if (propertyHelper == null) 
{ 
    propertyHelper = TypeDescriptor.GetProperties(item)[str]; 
} 
if (propertyHelper == null) 
{ 
    propertyHelper = this.GetPropertyHelper(ownerType, str); 
} 
if ((propertyHelper == null) && throwOnError) 
{ 
    throw new InvalidOperationException(SR.Get("PropertyPathNoProperty", new object[] { ownerType.Name, str })); 
} 
return propertyHelper; 

正如你所看到的,检索属性标识符(DependencyProperty的/ PropertyDescriptor的/的PropertyInfo)是这样的:

  1. 尝试得到的DependencyProperty,
  2. 如果项目实现ICustomTypeDescriptor,使用TypeDescriptor到获取PropertyDescriptor,
  3. 如果item实现INotifyPropertyChanged或是DependencyObject,请使用System.Reflection获取PropertyInfo,
  4. 否则使用TypeDescriptor获取PropertyDescriptor,
  5. 否则使用System.Reflection获取PropertyInfo,
  6. 否则抛出异常或返回null。

因此,如果item实现INotifyPropertyChanged接口,则System.Reflection/PropertyInfo优先于TypeDescriptor/PropertyDescriptor。我相信他们选择这种策略是出于性能方面的考虑,因为PropertyInfo比PropertyDescriptor轻得多。

解决您的问题的方法是实现ICustomTypeDescriptor(最好是明确地),以便它将ICustomTypeDescriptor方法调用转移到适当的TypeDescriptor方法调用,但不与对象参数但Type类型参数(this.GetType())。这样您的TypeDescriptionProvider将被使用。

+0

非常好的研究,谢谢。 –

相关问题