簡體   English   中英

為什么WPF似乎在實現INotifyPropertyChanged時繞過TypeDescriptionProviderAttribute?

[英]Why does WPF seem to bypass TypeDescriptionProviderAttribute when INotifyPropertyChanged is implemented?

我正在嘗試使用[TypeDescriptionProviderAttribute]以便為我的類提供自定義類型描述符。 這是有效的,但是當我實現INotifyPropertyChanged WPF似乎忽略了自定義類型描述符並直接進入CLR屬性(如果它存在)。 這是一個片段,稍后我將粘貼完整的示例:

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

我將TextBlock綁定到TheProperty。 當我...

  • 留下所有評論

    我按預期看到了“CLR - TheProperty”。

  • 使用[TypeDescriptionProvider]

    我按預期看到了“MyPropertyDescriptor - TheProperty”。

  • 使用ICustomTypeDescriptor

    我按預期看到了“MyPropertyDescriptor - TheProperty”。

  • 使用ICustomTypeDescriptorINotifyPropertyChanged

    我按預期看到了“MyPropertyDescriptor - TheProperty”。

  • 使用[TypeDescriptionProvider]INotifyPropertyChanged

    我看到“CLR - TheProperty”。 為什么是這樣? 奇怪的是,正常顯示沒有 CLR屬性的自定義屬性。 我的自定義類型描述符也返回一個“MyPropertyDescriptor - AnotherProperty”,它適用於所有情況,因為沒有定義CLR AnotherProperty

總之,鑒於此XAML

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

AnotherProperty始終按預期工作,因為該模型沒有名為“AnotherProperty”的CLR屬性。 除了使用[TypeDescriptionProvider]INotifyPropertyChanged 之外TheProperty按預期工作。

這是完整的代碼。 它有點長,但大部分都是無關緊要的,它只是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;
    }
}

老問題,但對於尋找答案的人來說..

問題出在System.Windows.PropertyPath.ResolvePropertyName(String,Object,Type,Object,Boolean)私有方法中。 我在.NET 4.0中的PresentationFramework.dll中找到了它。

從.NET Reflector中提取:

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. 如果item實現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。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM