Witamy w cudownym świecie System.ComponentModel. Ten ciemny zakątek .NET jest bardzo potężny, ale bardzo złożony.
Słowo ostrożności; chyba że masz na to dużo czasu - możesz zrobić to dobrze, po prostu serializować go w jakimkolwiek mechanizmie, z którego jesteś zadowolony, ale z powrotem nawodnić go z powrotem na DataTable
na każdym końcu ... co następuje nie jest dla osób o słabych nerwach; p
pierwsze - wiązania danych (tabel) działa przeciwko list (IList
/IListSource
) - tak List<T>
powinny być dobre (edycja: I misread coś). Ale nie zrozumiemy, że twój słownik to kolumny ...
Aby uzyskać typ, który będzie udawał kolumny, musisz użyć niestandardowych implementacji PropertyDescriptor
. Istnieje kilka sposobów, aby to zrobić, w zależności od tego, czy definicje kolumn są zawsze takie same (ale określane w czasie wykonywania, tj. Prawdopodobnie z konfiguracji), czy też zmieniają się w zależności od użycia (tak, jak każda instancja DataTable
może mieć różne kolumny).
Dla „na przykład” personalizacji, trzeba spojrzeć na ITypedList
- tej bestii (realizowany w Ponadto do IList
) ma za zadanie zabawy prezentuje właściwości dla danych tabelarycznych ... ale to nie jest sam:
For „za typ” personalizacji, można spojrzeć na TypeDescriptionProvider
- może to sugerować właściwości dynamiczne dla klasy ...
... czy można wdrożyć ICustomTypeDescriptor
- ale ten jest używany tylko (na listach) w bardzo okoliczności okazjonalne (indeksowanie obiektów (public object this[int index] {get;}
") i co najmniej jeden wiersz na liście w punkcie wiązania). (ten interfejs jest o wiele bardziej przydatny przy wiązaniu dyskretnych obiektów - tzn. nie list).
Wdrażanie ITypedList
i dostarczanie modelu PropertyDescriptor
to ciężka praca ... dlatego jest wykonywana tylko bardzo rzadko. Znam go dość dobrze, ale nie zrobiłbym tego tylko dla śmiechu ...
Oto bardzo uproszczony realizacja (wszystkie kolumny są ciągi, bez powiadomienia (przez deskryptor), brak walidacji (IDataErrorInfo
), bez konwersji (TypeConverter
), bez dodatkowego wsparcia lista (IBindingList
/IBindingListView
) , bez abstrakcji (IListSource
), żadnych innych innych metadanych/atrybutów itp.):
using System.ComponentModel;
using System.Collections.Generic;
using System;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
PropertyBagList list = new PropertyBagList();
list.Columns.Add("Foo");
list.Columns.Add("Bar");
list.Add("abc", "def");
list.Add("ghi", "jkl");
list.Add("mno", "pqr");
Application.Run(new Form {
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = list
}
}
});
}
}
class PropertyBagList : List<PropertyBag>, ITypedList
{
public PropertyBag Add(params string[] args)
{
if (args == null) throw new ArgumentNullException("args");
if (args.Length != Columns.Count) throw new ArgumentException("args");
PropertyBag bag = new PropertyBag();
for (int i = 0; i < args.Length; i++)
{
bag[Columns[i]] = args[i];
}
Add(bag);
return bag;
}
public PropertyBagList() { Columns = new List<string>(); }
public List<string> Columns { get; private set; }
PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
if(listAccessors == null || listAccessors.Length == 0)
{
PropertyDescriptor[] props = new PropertyDescriptor[Columns.Count];
for(int i = 0 ; i < props.Length ; i++)
{
props[i] = new PropertyBagPropertyDescriptor(Columns[i]);
}
return new PropertyDescriptorCollection(props, true);
}
throw new NotImplementedException("Relations not implemented");
}
string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
{
return "Foo";
}
}
class PropertyBagPropertyDescriptor : PropertyDescriptor
{
public PropertyBagPropertyDescriptor(string name) : base(name, null) { }
public override object GetValue(object component)
{
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value)
{
((PropertyBag)component)[Name] = (string)value;
}
public override void ResetValue(object component)
{
((PropertyBag)component)[Name] = null;
}
public override bool CanResetValue(object component)
{
return true;
}
public override bool ShouldSerializeValue(object component)
{
return ((PropertyBag)component)[Name] != null;
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return typeof(PropertyBag); }
}
}
class PropertyBag
{
private readonly Dictionary<string, string> values
= new Dictionary<string, string>();
public string this[string key]
{
get
{
string value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}
GridControl? Czy masz na myśli DataGridView? –