WPF 数据绑定 - “自定义类型描述符”示例
Posted
技术标签:
【中文标题】WPF 数据绑定 - “自定义类型描述符”示例【英文标题】:WPF Data Binding - Example of "Custom Type Descriptor" 【发布时间】:2010-12-22 12:15:23 【问题描述】:我看到有几个人说 WPF 可以将“自定义类型描述符”用于“更改通知”。
我知道如何进行更改通知的方法是:
object.GetBindingExpression(Bound.property).UpdateTarget();
或者让我的对象实现INotifiyPropertyChanged
。
我看到 cmets 说自定义类型描述符也可以工作,但没有人给出一个很好的例子来说明它是如何工作的。我现在要求那个例子(IE 是 WPF 数据绑定和通过自定义类型描述符更新的一个很好的例子。)
【问题讨论】:
【参考方案1】:这是一个非常简单的例子。
Window1.xaml:
<Window x:Class="CTDExample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock>Name:</TextBlock>
<TextBox Grid.Column="1" Text="Binding Name"/>
<TextBlock Grid.Row="1">Age:</TextBlock>
<TextBox Grid.Row="1" Grid.Column="1" Text="Binding Age"/>
<TextBlock Grid.Row="2" Grid.ColumnSpan="2">
<TextBlock.Text>
<MultiBinding StringFormat="0 is 1 years old.">
<Binding Path="Name"/>
<Binding Path="Age"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
Window1.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace CTDExample
public partial class Window1 : Window
public Window1()
InitializeComponent();
var ctd = new CTD();
ctd.AddProperty("Name");
ctd.AddProperty("Age");
DataContext = ctd;
public class CTD : CustomTypeDescriptor
private static readonly ICollection<PropertyDescriptor> _propertyDescriptors = new List<PropertyDescriptor>();
public void AddProperty(string name)
_propertyDescriptors.Add(new MyPropertyDescriptor(name));
public override PropertyDescriptorCollection GetProperties()
return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
return GetProperties();
public override EventDescriptorCollection GetEvents()
return null;
public override EventDescriptorCollection GetEvents(Attribute[] attributes)
return null;
public class MyPropertyDescriptor : PropertyDescriptor
private readonly IDictionary<object, object> _values;
public MyPropertyDescriptor(string name)
: base(name, null)
_values = new Dictionary<object, object>();
public override bool CanResetValue(object component)
throw new NotImplementedException();
public override Type ComponentType
get throw new NotImplementedException();
public override object GetValue(object component)
object value = null;
_values.TryGetValue(component, out value);
return value;
public override bool IsReadOnly
get return false;
public override Type PropertyType
get return typeof(object);
public override void ResetValue(object component)
throw new NotImplementedException();
public override void SetValue(object component, object value)
var oldValue = GetValue(component);
if (oldValue != value)
_values[component] = value;
OnValueChanged(component, new PropertyChangedEventArgs(base.Name));
public override bool ShouldSerializeValue(object component)
throw new NotImplementedException();
public override void AddValueChanged(object component, EventHandler handler)
// set a breakpoint here to see WPF attaching a value changed handler
base.AddValueChanged(component, handler);
【讨论】:
【参考方案2】:我使用Kent Boogart 的出色且非常清晰的示例作为我的自定义类型的基础。
我认为应该对示例程序进行一些小改动,以阐明CustomTypeDescriptor
和PropertyDescriptor
之间的关系。
-
我认为数据应该存储在类型对象的实例上,而不是属性描述符上。
通常我希望每个自定义类型实例都保留它自己的属性描述符集合,而不是静态的。为了澄清这一点,我添加了更多信息(
Type
)来键入属性描述符。
第二点确实是域问题,但我希望更典型的使用需要实例属性数据,因为在编译时不知道属性时会使用这种类型。
MainWindow.xaml
<Window
x:Class="CTDExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock>Name:</TextBlock>
<TextBox Grid.Column="1" Text="Binding Name"/>
<TextBlock Grid.Row="1">Age:</TextBlock>
<TextBox Grid.Row="1" Grid.Column="1" Text="Binding Age"/>
<TextBlock Grid.Row="2" Grid.ColumnSpan="2">
<TextBlock.Text>
<MultiBinding StringFormat="0 is 1 years old.">
<Binding Path="Name"/>
<Binding Path="Age"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace CTDExample
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
var ctd = new MyCustomType();
ctd.AddProperty("Name", typeof(string)); // Now takes a Type argument.
ctd.AddProperty("Age", typeof(int));
DataContext = ctd;
MyCustomType.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace CTDExample
public class MyCustomType : CustomTypeDescriptor
// This is instance data.
private readonly ICollection<PropertyDescriptor> _propertyDescriptors = new List<PropertyDescriptor>();
// The data is stored on the type instance.
private readonly IDictionary<string, object> _propertyValues = new Dictionary<string, object>();
// The property descriptor now takes an extra argument.
public void AddProperty(string name, Type type)
_propertyDescriptors.Add(new MyPropertyDescriptor(name, type));
public override PropertyDescriptorCollection GetProperties()
return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
return GetProperties();
public override EventDescriptorCollection GetEvents()
return null;
public override EventDescriptorCollection GetEvents(Attribute[] attributes)
return null;
private class MyPropertyDescriptor : PropertyDescriptor
// This data is here to indicate that different instances of the type
// object may have properties of the same name, but with different
// characteristics.
private readonly Type _type;
public MyPropertyDescriptor(string name, Type type)
: base(name, null)
_type = type;
public override bool CanResetValue(object component)
throw new NotImplementedException();
public override Type ComponentType
get throw new NotImplementedException();
public override object GetValue(object component)
MyCustomType obj = (MyCustomType)component;
object value = null;
obj._propertyValues.TryGetValue(Name, out value);
return value;
public override bool IsReadOnly
get return false;
public override Type PropertyType
get return _type;
public override void ResetValue(object component)
throw new NotImplementedException();
public override void SetValue(object component, object value)
var oldValue = GetValue(component);
if (oldValue != value)
MyCustomType obj = (MyCustomType)component;
obj._propertyValues[Name] = value;
OnValueChanged(component, new PropertyChangedEventArgs(Name));
public override bool ShouldSerializeValue(object component)
throw new NotImplementedException();
public override void AddValueChanged(object component, EventHandler handler)
// set a breakpoint here to see WPF attaching a value changed handler
base.AddValueChanged(component, handler);
我希望我没有发出任何咆哮,因为这是我的第一篇文章!
【讨论】:
以上是关于WPF 数据绑定 - “自定义类型描述符”示例的主要内容,如果未能解决你的问题,请参考以下文章