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

非常好的研究,謝謝。 –

相關問題