2009-09-23 9 views
6

लागू करता है मैं डेटाग्रिड व्यू में ऑब्जेक्ट्स की एक सूची प्रदर्शित कर रहा हूं। सब ठीक काम कर रहा था। ऑब्जेक्ट्स के गुणों के आधार पर कॉलम डेटाग्रिड व्यू में स्वचालित रूप से जोड़े गए थे।DataGridView ऑब्जेक्ट्स की उचित वस्तुएं नहीं दिखा रहा है जो ICustomTypeDescriptor

अब मैंने ICustomTypeDescriptor को लागू करने के लिए ग्रिड में प्रदर्शित कक्षा को बदल दिया है। लेकिन अब जब मैं इसे अपने कस्टम ऑब्जेक्ट की सूची में डेटासोर्स सेट करता हूं तो ग्रिड अब कोई कॉलम या पंक्तियां नहीं दिखाता है।

मुझे लगता है कि इस तथ्य के साथ कुछ करना है कि ICustomTypeDescriptor प्रत्येक ग्रिड की प्रत्येक पंक्ति में दिखाया गया प्रत्येक उदाहरण गुणों का एक अलग सेट लौटा सकता है।

मैं ICustomTypeDescriptor लागू कर रहा हूं ताकि मैं उपयोगकर्ताओं को गतिशील रूप से रन टाइम पर ऑब्जेक्ट्स में कस्टम गुण जोड़ने की अनुमति दे सकूं। ये कस्टम गुण डेटाग्रिड व्यू के माध्यम से दृश्यमान और संपादन योग्य होना चाहिए।

क्यों DataGridView मेरी ICustomTypeDescriptor तरीकों को नहीं देख पा रहा है? क्या कोई और तरीका है कि मैं गतिशील रूप से किसी ऑब्जेक्ट में गुण जोड़ सकता हूं जो डेटाग्रिड व्यू में प्रदर्शित किया जाएगा?

उत्तर

21

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> (या इसी तरह) का उपयोग कर रहे हैं, तो आप "सरलतम" (आईएमओ) केस - # 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); } 
     } 
    } 
} 
संबंधित मुद्दे