gpt4 book ai didi

c# - DataGridView 不显示实现 ICustomTypeDescriptor 的对象的属性

转载 作者:太空狗 更新时间:2023-10-29 20:48:04 27 4
gpt4 key购买 nike

我在 DataGridView 中显示对象列表。一切正常。根据对象的属性自动将列添加到 DataGridView

现在我更改了我在网格中显示的类以实现 ICustomTypeDescriptor。但是现在,当我将其 DataSource 设置为我的自定义对象列表时,网格不再显示任何列或行。

我猜这与以下事实有关:使用 ICustomTypeDescriptor,每个网格的每一行中显示的每个实例都可能返回一组不同的属性。

我正在实现 ICustomTypeDescriptor,这样我就可以允许用户在运行时向对象动态添加自定义属性。这些自定义属性应该通过 DataGridView 可见和可编辑。

为什么 DataGridView 看不到我的 ICustomTypeDescriptor 方法?是否有另一种方法可以动态地将属性添加到将显示在 DataGridView 中的对象?

最佳答案

DataGridView查看元数据的列表版本;规则是……复杂的:

  1. 如果数据源实现了IListSource , GetList()被评估并用作数据源(在 2 处继续)
  2. 如果数据源实现了ITypedList , GetProperties()用于获取元数据(退出)
  3. 如果可以找到类型化(非 object )索引器(即 public T this[int index] ),则 T通过 TypeDescriptor.GetProperties(type) 用作来源:
    1. 如果 TypeDescriptionProvider已分配,这用于针对类型的元数据(退出)
    2. 否则反射用于元数据(退出)
  4. 如果列表非空,第一个对象通过TypeDescriptor.GetProperties(list[0])用于元数据:
    1. 如果ICustomTypeDescriptor被实现,然后被使用(退出)[*]
    2. 如果 TypeDescriptionProvider已分配,这用于针对类型的元数据(退出)[*]
    3. 否则使用反射(退出)
  5. 否则元数据不可用(退出)

([*]=我不记得这两个人走了哪条路...)

如果您使用 List<T> (或类似的),那么你遇到了“最简单的”(IMO)案例 - #3。如果您想提供自定义元数据,那么;你最好的选择是写一个 TypeDescriptionProvider并将其与类型相关联。我可以写一个例子,但这需要一段时间(可能在火车上)...

编辑:here's使用 ITypedList 的示例;我会尝试调整它以使用 TypeDescriptionProvider相反……

第二次编辑:使用 TypeDescriptionProvider 的完整(但最小)示例跟随;长代码警告...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
// example
static class Program {
[STAThread]
static void Main() {
PropertyBag.AddProperty("UserName", typeof(string), new DisplayNameAttribute("User Name"));
PropertyBag.AddProperty("DateOfBirth", typeof(DateTime), new DisplayNameAttribute("Date of Birth"));
BindingList<PropertyBag> list = new BindingList<PropertyBag>() {
new PropertyBag().With("UserName", "Fred").With("DateOfBirth", new DateTime(1998,12,1)),
new PropertyBag().With("UserName", "William").With("DateOfBirth", new DateTime(1997,4,23))
};

Application.Run(new Form {
Controls = {
new DataGridView { // prove it works for complex bindings
Dock = DockStyle.Fill,
DataSource = list,
ReadOnly = false, AllowUserToAddRows = true
}
},
DataBindings = {
{"Text", list, "UserName"} // prove it works for simple bindings
}
});
}
}
// PropertyBag file 1; the core bag
partial class PropertyBag : INotifyPropertyChanged {
private static PropertyDescriptorCollection props;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
static PropertyBag() {
props = new PropertyDescriptorCollection(new PropertyDescriptor[0], true);
// init the provider; I'm avoiding TypeDescriptionProviderAttribute so that we
// can exploit the default implementation for fun and profit
TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(PropertyBag)),
customProvider = new PropertyBagTypeDescriptionProvider(defaultProvider);
TypeDescriptor.AddProvider(customProvider, typeof(PropertyBag));
}
private static readonly object syncLock = new object();
public static void AddProperty(string name, Type type, params Attribute[] attributes) {
lock (syncLock)
{ // append the new prop, into a *new* collection, so that downstream
// callers don't have to worry about the complexities
PropertyDescriptor[] newProps = new PropertyDescriptor[props.Count + 1];
props.CopyTo(newProps, 0);
newProps[newProps.Length - 1] = new PropertyBagPropertyDescriptor(name, type, attributes);
props = new PropertyDescriptorCollection(newProps, true);
}
}
private readonly Dictionary<string, object> values;
public PropertyBag()
{ // mainly want to enforce that we have a public parameterless ctor
values = new Dictionary<string, object>();
}
public object this[string key] {
get {
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
object value;
values.TryGetValue(key, out value);
return value;
}
set {
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
var prop = props[key];
if (prop == null) throw new ArgumentException("Invalid property: " + key, "key");
values[key] = value;
OnPropertyChanged(key);
}
}
internal void Reset(string key) {
values.Remove(key);
}
internal bool ShouldSerialize(string key) {
return values.ContainsKey(key);
}
}

static class PropertyBagExt
{
// cheeky fluent API to make the example code easier:
public static PropertyBag With(this PropertyBag obj, string name, object value) {
obj[name] = value;
return obj;
}
}

// PropertyBag file 2: provider / type-descriptor
partial class PropertyBag {
class PropertyBagTypeDescriptionProvider : TypeDescriptionProvider, ICustomTypeDescriptor {
readonly ICustomTypeDescriptor defaultDescriptor;
public PropertyBagTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) {
this.defaultDescriptor = parent.GetTypeDescriptor(typeof(PropertyBag));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) {
return this;
}
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
return defaultDescriptor.GetAttributes();
}
string ICustomTypeDescriptor.GetClassName() {
return defaultDescriptor.GetClassName();
}
string ICustomTypeDescriptor.GetComponentName() {
return defaultDescriptor.GetComponentName();
}
TypeConverter ICustomTypeDescriptor.GetConverter() {
return defaultDescriptor.GetConverter();
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
return defaultDescriptor.GetDefaultEvent();
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
return defaultDescriptor.GetDefaultProperty();
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
return defaultDescriptor.GetEditor(editorBaseType);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
return defaultDescriptor.GetEvents(attributes);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
return defaultDescriptor.GetEvents();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
return PropertyBag.props; // should really be filtered, but meh!
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
return PropertyBag.props;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
return defaultDescriptor.GetPropertyOwner(pd);
}
}
}
// PropertyBag file 3: property descriptor
partial class PropertyBag {
class PropertyBagPropertyDescriptor : PropertyDescriptor {
private readonly Type type;
public PropertyBagPropertyDescriptor(string name, Type type, Attribute[] attributes)
: base(name, attributes) {
this.type = type;
}
public override object GetValue(object component) {
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value) {
((PropertyBag)component)[Name] = value;
}
public override void ResetValue(object component) {
((PropertyBag)component).Reset(Name);
}
public override bool CanResetValue(object component) {
return true;
}
public override bool ShouldSerializeValue(object component) {
return ((PropertyBag)component).ShouldSerialize(Name);
}
public override Type PropertyType {
get { return type; }
}
public override bool IsReadOnly {
get { return false; }
}
public override Type ComponentType {
get { return typeof(PropertyBag); }
}
}
}

关于c# - DataGridView 不显示实现 ICustomTypeDescriptor 的对象的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1468840/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com