![](/img/trans.png)
[英]How to Update bindings when an object with no INotifyPropertyChanged implemented. WPF
[英]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”。
使用ICustomTypeDescriptor
和INotifyPropertyChanged
我按預期看到了“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)如下所示:
因此,如果item實現了INotifyPropertyChanged接口,則System.Reflection / PropertyInfo優先於TypeDescriptor / PropertyDescriptor。 我認為他們出於性能原因選擇了這種策略,因為PropertyInfo比PropertyDescriptor輕得多。
您的問題的解決方案是實現ICustomTypeDescriptor(最好是顯式的),以便它將ICustomTypeDescriptor方法調用傳遞給適當的TypeDescriptor方法調用,但不傳輸對象參數,但是Type參數(this.GetType())。 這樣就可以使用TypeDescriptionProvider。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.