我正在试验ICustomTypeDescriptor
接口和PropertyDescriptor
类,以便在对象上创建动态属性。我在简单对象方面取得了很大成功,但我无法让嵌套对象创建它们的动态属性?
例如,在下面的数据绑定对话框中,我将我的 Person 类添加为StaticResource
,然后尝试将数据绑定Person.Child.Name
到测试框:
对于Person.Child
我希望看到我动态创建的属性(Name
和Age
),但正如您所见,它没有按预期工作?就好像数据绑定对话框没有ICustomTypeDescriptor
询问Person.Child
?
有关如何使这些嵌套属性“可见”的任何指导?
外层
public class Person : ICustomTypeDescriptor, INotifyPropertyChanged
{
private readonly List<CustomPropertyDescriptor> propertyDescriptors = new List<CustomPropertyDescriptor>();
private readonly Dictionary<string, object> properties = new Dictionary<string, object>();
public Person()
{
// 'Dynamic' Property
string name = "Name";
object value = "Person's Name";
this.properties.Add(name, value);
var propertyDescriptor = new CustomPropertyDescriptor(
typeof(Person),
name,
value,
value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
this.propertyDescriptors.Add(propertyDescriptor);
// 'Dynamic' Property
name = "Child";
value = new Child();
this.properties.Add(name, value);
propertyDescriptor = new CustomPropertyDescriptor(
typeof(Child),
name,
value,
value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
this.propertyDescriptors.Add(propertyDescriptor);
propertyDescriptor.PropertyChanged += this.PropertyDescriptorPropertyChanged;
}
public event PropertyChangedEventHandler PropertyChanged;
// Test Property (shouldn't be visible)
public string NotDynamic { get; set; }
public override string ToString()
{
return string.Format("{0} ({1})", this.properties["Name"], this.properties["Age"]);
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
try
{
return this.propertyDescriptors.First();
}
catch (InvalidOperationException)
{
return null;
}
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(this.propertyDescriptors.ToArray());
}
public PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(null);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
protected void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private void PropertyDescriptorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.OnPropertyChanged(e.PropertyName);
}
}
内部类
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Child : ICustomTypeDescriptor, INotifyPropertyChanged
{
private readonly List<CustomPropertyDescriptor> propertyDescriptors = new List<CustomPropertyDescriptor>();
private readonly Dictionary<string, object> properties = new Dictionary<string, object>();
public Child()
{
// 'Dynamic' Property
string name = "Name";
object value = "Person's Child";
this.properties.Add(name, value);
var propertyDescriptor = new CustomPropertyDescriptor(
typeof(Person),
name,
value,
value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
propertyDescriptor.PropertyChanged += this.PropertyDescriptorPropertyChanged;
this.propertyDescriptors.Add(propertyDescriptor);
}
public event PropertyChangedEventHandler PropertyChanged;
// Test Property (shouldn't be visible)
public string NotDynamic { get; set; }
public override string ToString()
{
return string.Format("{0} ({1})", this.properties["Name"], this.properties["Age"]);
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
try
{
return this.propertyDescriptors.First();
}
catch (InvalidOperationException)
{
return null;
}
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(this.propertyDescriptors.ToArray());
}
public PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(null);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
protected void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private void PropertyDescriptorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.OnPropertyChanged(e.PropertyName);
}
}
属性描述符
public class CustomPropertyDescriptor : PropertyDescriptor, INotifyPropertyChanged
{
private readonly Type componentType;
private string name;
private object value;
public CustomPropertyDescriptor(Type componentType, string name, object value, Attribute[] attributes)
: base(name, attributes)
{
this.componentType = componentType;
this.name = name;
this.value = value;
}
public event PropertyChangedEventHandler PropertyChanged;
public override bool IsBrowsable
{
get
{
return true;
}
}
public override Type ComponentType
{
get { return this.componentType; }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return this.value.GetType(); }
}
public override object GetValue(object component)
{
return this.value;
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
this.value = value;
this.OnPropertyChanged(this.Name);
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
private void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}