2015-09-21 62 views
1

我試圖使用DataGridView來顯示對象列表。 在我想呈現屬性的類中,我有一些C#屬性,並且我也想動態地創建屬性,原因有些。試圖將DataGridView與ICustomTypeDescriptor一起使用

這裏我有一個例子,它適用於C#屬性(FeatureId),但動態創建的屬性(Name)返回所有實例的第一個實例的值。 爲什麼?

首先實現了ICustomPropertyDescriptor接口的類

public abstract class PropertyPresentationSubBase : ICustomTypeDescriptor 
{ 
public String GetClassName() 
{ 
    return TypeDescriptor.GetClassName(this, true); 
} 

public AttributeCollection GetAttributes() 
{ 
    return TypeDescriptor.GetAttributes(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() 
{ 
    return TypeDescriptor.GetDefaultProperty(this, true); 
} 

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 virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
{ 
    PropertyDescriptorCollection rtn = TypeDescriptor.GetProperties(this); 

    //rtn = FilterReadonly(rtn, attributes); 

    return new PropertyDescriptorCollection(rtn.Cast<PropertyDescriptor>().ToArray()); 


} 

public virtual PropertyDescriptorCollection GetProperties() 
{ 

    return TypeDescriptor.GetProperties(this, true); 

} 


public object GetPropertyOwner(PropertyDescriptor pd) 
{ 
    return this; 
} 

[Browsable(false)] 
public PropertyPresentationSubBase Parent 
{ 
    get 
    { 
    return m_Parent; 
    } 
    set 
    { 
    m_Parent = value; 
    } 
} 

PropertyPresentationSubBase m_Parent = null; 

[Browsable(false)] 
public Type ValueType 
{ 
    get 
    { 
    return valueType; 
    } 
    set 
    { 
    valueType = value; 
    } 
} 

private Type valueType = null; 

[Browsable(false)] 
public string Name 
{ 
    get 
    { 
    return sName; 
    } 
    set 
    { 
    sName = value; 
    } 
} 



public abstract object GetValue(); 

private string sName = string.Empty; 

public abstract void Change(object value); 


    } 
} 

我還具有從的PropertyDescriptor

public class MyCustomPropertyDescriptor : PropertyDescriptor 
{ 
PropertyPresentationSubBase m_Property; 

public MyCustomPropertyDescriptor(PropertyPresentationSubBase myProperty, Attribute[] attrs, int propertyNo) 
    : base(myProperty.Name + propertyNo, attrs) 
{ 
    m_Property = myProperty; 
} 

#region PropertyDescriptor specific 

public override bool CanResetValue(object component) 
{ 
    return false; 
} 

public override string Name 
{ 
    get 
    { 
    return "MyName"; 
    } 
} 

public override Type ComponentType 
{ 
    get 
    { 
    return null; 
    } 
} 

public override object GetValue(object component) 
{ 
    return m_Property.GetValue(); 
} 


public override string Description 
{ 
    get 
    { 
    return "Description"; 
    } 
} 



public object Value 
{ 
    get 
    { 
    return m_Property; 
    } 
} 


public override string Category 
{ 
    get 
    { 
    return "Category"; 
    } 
} 

public override string DisplayName 
{ 
    get 
    { 
    return m_Property.Name; 
    } 

} 


public override bool IsReadOnly 
{ 
    get 
    { 
    return false; 
    } 
} 


public override void ResetValue(object component) 
{ 
    //Have to implement 
} 

public override bool ShouldSerializeValue(object component) 
{ 
    return false; 
} 




public override void SetValue(object component, object value) 
{ 
    m_Property.Change(value); 
} 

public override Type PropertyType 
{ 

    get 
    { 
    if ((m_Property != null) && (m_Property.ValueType != null)) 
    { 
     return m_Property.ValueType; 
    } 
    else 
    { 
     return System.Type.Missing.GetType(); 

    } 
    } 
} 

#endregion 

}

保存數據的小型類繼承的類:

public class QuadriFeatureItem 
{ 
public QuadriFeatureItem(int featureId, string name) 
{ 
    m_featureId = featureId; 
    m_name = name; 

} 
public int m_featureId; 

public string m_name; 
} 

我的類被髮送到網格(同時含有FEATUREID屬性和動態創建的屬性)

class FeaturePropertyPresentation : PropertyPresentationSubBase 
{ 

public int FeatureId 
{ 
    get 
    { 
    return m_feature.m_featureId; 

    } 
    set { m_feature.m_featureId = value; } 
} 

public FeaturePropertyPresentation(QuadriFeatureItem item) 
{ 
    m_feature = item; 
} 

private QuadriFeatureItem m_feature; 

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
{ 
    PropertyDescriptorCollection rtn = base.GetProperties(attributes); 

    CreateNameAttribute(ref rtn, attributes); 

    return rtn; 

} 

private void CreateNameAttribute(ref PropertyDescriptorCollection pdc, Attribute[] attributes) 
{ 

    NameProperty namePres = null; 
    namePres = new NameProperty(m_feature, this); 

    pdc.Add(new MyCustomPropertyDescriptor(namePres, attributes, pdc.Count)); 

} 

public override void Change(object value) 
{ 
    throw new NotImplementedException(); 
} 

public override object GetValue() 
{ 
    return this; 
} 

}

實現該nameproperty一類:

class NameProperty : PropertyPresentationSubBase 
{ 

public NameProperty(QuadriFeatureItem feature, FeaturePropertyPresentation parent) 
    : base() 
{ 
    m_quadriFeatureItem = feature; 
    Parent = parent; 
    ValueType = typeof(string); 

} 

private QuadriFeatureItem m_quadriFeatureItem; 

public override void Change(object value) 
{ 
    m_quadriFeatureItem.m_name = (string)value; 
} 

public override object GetValue() 
{ 

    return m_quadriFeatureItem.m_name; 

} 

} 

而我的表單代碼:

public Form1() 
{ 
    InitializeComponent(); 

    ShowGrid(); 
} 

private void ShowGrid() 
{ 

    QuadriFeatureItem no1 = new QuadriFeatureItem(1, "Nummer1"); 
    QuadriFeatureItem no2 = new QuadriFeatureItem(2, "Nummer2"); 
    QuadriFeatureItem no3 = new QuadriFeatureItem(3, "Nummer3"); 

    BindingSource source = new BindingSource(); 

    FeaturePropertyPresentation no1Pres = new FeaturePropertyPresentation(no1); 
    FeaturePropertyPresentation no2Pres = new FeaturePropertyPresentation(no2); 
    FeaturePropertyPresentation no3Pres = new FeaturePropertyPresentation(no3); 

    source.Add(no1Pres); 
    source.Add(no2Pres); 
    source.Add(no3Pres); 

    dataGridView1.DataSource = source; 

    Show();  
} 

但網格對所有行顯示「Nummer1」。爲什麼?我在PropertyGrid中使用這個表示類,它工作正常。我也在PropertyGrid中使用這個MyCustomPropertyDescriptor。

我希望現在能夠在datagridview中重用這個presentationclasses和MyCustomPropertyDescriptor。是否可以在MyCustomPropertyDescriptor或PropertyPresentationSubBase中進行任何修改?

+0

也許它與'ICustomTypeDescriptor'的實現有關,所以最好發佈你的實現。 –

+0

這絕對是可能的,但你需要展示你的實現。 –

+0

如何顯示「MyCustomPropertyDescriptor」的代碼?看,如果你需要幫助,你需要向我們提供足夠的信息。我只能說目前的問題在於你的代碼。 –

回答

0

問題是您的自定義屬性描述符綁定到具體實例。當您使用單個項目數據綁定(如TextBox到您的對象屬性或在PropertyGrid控件中選擇您的對象)時,這起作用。但是,當您使用控件需要列表數據綁定(如DataGridView,ListView,ListBox,ComboBox列表等)此技術不起作用。爲了自動填充列,DataGridView需要一組屬性通用到所有項目。爲了做到這一點,它嘗試了幾種方法來獲取這些信息(可以在這裏找到一個很好的解釋DataGridView not showing properites of objects which implement ICustomTypeDescriptor),其中一個方法是獲取列表中的第一項並詢問屬性(因此可以調試您的調試體驗)。無論如何,爲了使這個工作在列表綁定方案中,你的屬性描述符需要以不同的方式實現。

請注意PropertyDescriptor s GetValue/SetValue方法的簽名。他們都有一個參數object component。這是您需要返回或設置值的對象實例。你可以將屬性描述符想象成與我們通常在編程語言中使用的反例。因此,而不是

var val = obj.Property; 
obj.Property = val; 

我們

var val = propertyDescriptor.GetValue(obj); 
propertyDescriptor.SetValue(obj, val); 

換句話說,你不應該「嵌入」的屬性描述符中的對象實例,但使用傳遞的參數。

下面是一個簡單通用的實現屬性描述的正是這樣做的:用你的東西

public class SimplePropertyDescriptor<TComponent, TValue> : PropertyDescriptor 
    where TComponent : class 
{ 
    private readonly Func<TComponent, TValue> getValue; 
    private readonly Action<TComponent, TValue> setValue; 
    private readonly string displayName; 
    public SimplePropertyDescriptor(string name, Attribute[] attrs, Func<TComponent, TValue> getValue, Action<TComponent, TValue> setValue = null, string displayName = null) 
     : base(name, attrs) 
    { 
     Debug.Assert(getValue != null); 
     this.getValue = getValue; 
     this.setValue = setValue; 
     this.displayName = displayName; 
    } 
    public override string DisplayName { get { return displayName ?? base.DisplayName; } } 
    public override Type ComponentType { get { return typeof(TComponent); } } 
    public override bool IsReadOnly { get { return setValue == null; } } 
    public override Type PropertyType { get { return typeof(TValue); } } 
    public override bool CanResetValue(object component) { return false; } 
    public override bool ShouldSerializeValue(object component) { return false; } 
    public override void ResetValue(object component) { } 
    public override object GetValue(object component) { return getValue((TComponent)component); } 
    public override void SetValue(object component, object value) { setValue((TComponent)component, (TValue)value); } 
} 

示例用法:

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
{ 
    var properties = base.GetProperties(attributes); 
    // Custom name property 
    properties.Add(new SimplePropertyDescriptor<FeaturePropertyPresentation, string>("FeatureName", attributes, 
     getValue: component => component.m_feature.m_name, 
     setValue: (component, value) => component.m_feature.m_name = value, // remove this line to make it readonly 
     displayName: "Feature Name" 
    )); 
    return properties; 
} 

,並把他們放在一起,一個相當於小例子你的:

using System; 
using System.ComponentModel; 
using System.Diagnostics; 
using System.Linq; 
using System.Windows.Forms; 

namespace Samples 
{ 
    // Generic implemenation of a property descriptor 
    public class SimplePropertyDescriptor<TComponent, TValue> : PropertyDescriptor 
     where TComponent : class 
    { 
     private readonly Func<TComponent, TValue> getValue; 
     private readonly Action<TComponent, TValue> setValue; 
     private readonly string displayName; 
     public SimplePropertyDescriptor(string name, Attribute[] attrs, Func<TComponent, TValue> getValue, Action<TComponent, TValue> setValue = null, string displayName = null) 
      : base(name, attrs) 
     { 
      Debug.Assert(getValue != null); 
      this.getValue = getValue; 
      this.setValue = setValue; 
      this.displayName = displayName; 
     } 
     public override string DisplayName { get { return displayName ?? base.DisplayName; } } 
     public override Type ComponentType { get { return typeof(TComponent); } } 
     public override bool IsReadOnly { get { return setValue == null; } } 
     public override Type PropertyType { get { return typeof(TValue); } } 
     public override bool CanResetValue(object component) { return false; } 
     public override bool ShouldSerializeValue(object component) { return false; } 
     public override void ResetValue(object component) { } 
     public override object GetValue(object component) { return getValue((TComponent)component); } 
     public override void SetValue(object component, object value) { setValue((TComponent)component, (TValue)value); } 
    } 
    // Your stuff 
    public abstract class PropertyPresentationSubBase : ICustomTypeDescriptor 
    { 
     public string GetClassName() 
     { 
      return TypeDescriptor.GetClassName(this, true); 
     } 

     public AttributeCollection GetAttributes() 
     { 
      return TypeDescriptor.GetAttributes(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() 
     { 
      return TypeDescriptor.GetDefaultProperty(this, true); 
     } 

     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 virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
     { 
      PropertyDescriptorCollection rtn = TypeDescriptor.GetProperties(this); 

      //rtn = FilterReadonly(rtn, attributes); 

      return new PropertyDescriptorCollection(rtn.Cast<PropertyDescriptor>().ToArray()); 
     } 

     public virtual PropertyDescriptorCollection GetProperties() 
     { 

      return TypeDescriptor.GetProperties(this, true); 

     } 


     public object GetPropertyOwner(PropertyDescriptor pd) 
     { 
      return this; 
     } 

     [Browsable(false)] 
     public PropertyPresentationSubBase Parent 
     { 
      get 
      { 
       return m_Parent; 
      } 
      set 
      { 
       m_Parent = value; 
      } 
     } 

     PropertyPresentationSubBase m_Parent = null; 

     [Browsable(false)] 
     public Type ValueType 
     { 
      get 
      { 
       return valueType; 
      } 
      set 
      { 
       valueType = value; 
      } 
     } 

     private Type valueType = null; 

     [Browsable(false)] 
     public string Name 
     { 
      get 
      { 
       return sName; 
      } 
      set 
      { 
       sName = value; 
      } 
     } 



     public abstract object GetValue(); 

     private string sName = string.Empty; 

     public abstract void Change(object value); 
    } 
    public class QuadriFeatureItem 
    { 
     public QuadriFeatureItem(int featureId, string name) 
     { 
      m_featureId = featureId; 
      m_name = name; 

     } 
     public int m_featureId; 

     public string m_name; 
    } 
    class FeaturePropertyPresentation : PropertyPresentationSubBase 
    { 

     public int FeatureId 
     { 
      get 
      { 
       return m_feature.m_featureId; 

      } 
      set { m_feature.m_featureId = value; } 
     } 

     public FeaturePropertyPresentation(QuadriFeatureItem item) 
     { 
      m_feature = item; 
     } 

     private QuadriFeatureItem m_feature; 

     public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
     { 
      var properties = base.GetProperties(attributes); 
      // Custom name property 
      properties.Add(new SimplePropertyDescriptor<FeaturePropertyPresentation, string>("FeatureName", attributes, 
       getValue: component => component.m_feature.m_name, 
       setValue: (component, value) => component.m_feature.m_name = value, // remove this line to make it readonly 
       displayName: "Feature Name" 
      )); 
      return properties; 
     } 
     public override void Change(object value) 
     { 
      throw new NotImplementedException(); 
     } 

     public override object GetValue() 
     { 
      return this; 
     } 

    } 
    // Test 
    static class Test 
    { 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      var dataSet = Enumerable.Range(1, 10).Select(n => new FeaturePropertyPresentation(new QuadriFeatureItem(n, "Nummer" + n))).ToList(); 
      var form = new Form(); 
      var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form }; 
      dg.DataSource = dataSet; 
      Application.Run(form); 
     } 
    } 
} 

結果:

enter image description here

相關問題