2009-09-23 32 views
6

Tôi đang hiển thị danh sách các đối tượng trong một DataGridView. Mọi thứ đều hoạt động tốt. Các cột được tự động thêm vào DataGridView dựa trên các thuộc tính của các đối tượng.DataGridView không hiển thị sự thích hợp của các đối tượng thực hiện ICustomTypeDescriptor

Bây giờ tôi đã thay đổi lớp tôi đang hiển thị trong lưới để triển khai ICustomTypeDescriptor. Nhưng bây giờ lưới bây giờ không còn hiển thị bất kỳ cột hoặc hàng nào khi tôi đặt nó là DataSource vào danh sách đối tượng tùy chỉnh của tôi.

Tôi đoán việc này có liên quan đến thực tế rằng với ICustomTypeDescriptor mỗi trường hợp được hiển thị trong mỗi hàng của mỗi lưới có thể trả về một tập hợp thuộc tính khác.

Tôi đang triển khai ICustomTypeDescriptor để tôi có thể cho phép người dùng tự động thêm thuộc tính tùy chỉnh vào đối tượng trong thời gian chạy. Các thuộc tính tùy chỉnh này sẽ hiển thị và có thể chỉnh sửa thông qua DataGridView.

Tại sao DataGridView không thấy các phương thức ICustomTypeDescriptor của tôi? Có cách nào khác để tôi có thể tự động thêm các thuộc tính vào một đối tượng sẽ được hiển thị trong một DataGridView không?

Trả lời

21

DataGridView xem phiên bản danh sách siêu dữ liệu; các quy tắc cho điều này là ... phức tạp:

  1. nếu các dữ liệu mã nguồn thực hiện IListSource, GetList() được đánh giá và sử dụng như là dữ liệu nguồn (tiếp tục ở mức 2)
  2. nếu các dữ liệu mã nguồn thực hiện ITypedList, GetProperties() được sử dụng để thu thập siêu dữ liệu (thoát)
  3. nếu đánh máy (không object) indexer có thể được tìm thấy (ví dụ: public T this[int index]), sau đó T được sử dụng như là nguồn qua TypeDescriptor.GetProperties(type):
    1. nếu một TypeDescriptionProvider được gán, các này được sử dụng cho siêu dữ liệu chống lại các loại (thoát)
    2. khác phản ánh được sử dụng cho siêu dữ liệu (exit)
  4. nếu danh sách không bị để trống, đối tượng đầu tiên được sử dụng cho siêu dữ liệu qua TypeDescriptor.GetProperties(list[0]):
    1. nếu ICustomTypeDescriptor được thực hiện, sau đó nó được sử dụng (thoát) [*]
    2. nếu một TypeDescriptionProvider được gán, các này được sử dụng cho siêu dữ liệu chống lại các loại (thoát) [*]
    3. nếu ngược lại phản ánh được sử dụng (thoát)
  5. khác siêu dữ liệu là không có sẵn (exit)

([*] = Tôi không thể nhớ được con đường xung quanh hai đi ...)

Nếu bạn đang sử dụng List<T> (hoặc tương tự), thì bạn nhấn trường hợp "đơn giản nhất" (IMO) - # 3. Nếu bạn muốn cung cấp siêu dữ liệu tùy chỉnh, thì; bạn đặt cược tốt nhất là viết TypeDescriptionProvider và liên kết nó với loại. Tôi có thể viết một ví dụ nhưng sẽ mất một lúc (trên tàu, có lẽ) ...

Chỉnh sửa: here's ví dụ sử dụng ITypedList; Tôi sẽ cố gắng tinh chỉnh nó để sử dụng TypeDescriptionProvider thay vào đó ...

Chỉnh sửa lần thứ hai: ví dụ đầy đủ (chưa tối thiểu) sử dụng TypeDescriptionProvider theo sau; cảnh báo mã dài ...

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ác vấn đề liên quan