Tôi đã thực hiện chính xác điều này với giao diện ICustomTypeDescriptor và Từ điển.
Triển khai ICustomTypeDescriptor cho các thuộc tính động:
Gần đây tôi đã có yêu cầu ràng buộc một chế độ xem lưới với một đối tượng bản ghi có thể có bất kỳ số lượng thuộc tính nào có thể được thêm vào và loại bỏ trong thời gian chạy. Điều này là để cho phép người dùng thêm một cột mới vào tập kết quả để nhập một tập dữ liệu bổ sung.
Điều này có thể đạt được bằng cách đặt mỗi 'hàng' dữ liệu làm từ điển với khóa là tên thuộc tính và giá trị là một chuỗi hoặc một lớp có thể lưu trữ giá trị của thuộc tính cho hàng được chỉ định. Tất nhiên có một Danh sách các đối tượng Từ điển sẽ không thể bị ràng buộc vào một lưới. Đây là nơi ICustomTypeDescriptor xuất hiện.
Bằng cách tạo một lớp bao bọc cho Từ điển và làm cho nó tuân thủ giao diện ICustomTypeDescriptor, hành vi trả về các thuộc tính cho một đối tượng có thể bị ghi đè.
Hãy xem việc triển khai lớp 'hàng' dữ liệu bên dưới:
public class TestResultRowWrapper : Dictionary<string, TestResultValue>, ICustomTypeDescriptor
{
#region Methods
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return new AttributeCollection(null);
}
string ICustomTypeDescriptor.GetClassName()
{
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return new EventDescriptorCollection(null);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return new EventDescriptorCollection(null);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<propertydescriptor> properties = new List<propertydescriptor>();
foreach (string key in this.Keys)
{
properties.Add(new TestResultPropertyDescriptor(key));
}
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this.GetType(), attributes);
foreach (PropertyDescriptor oPropertyDescriptor in pdc)
{
properties.Add(oPropertyDescriptor);
}
return new PropertyDescriptorCollection(properties.ToArray());
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion Methods
}
Lưu ý: Trong phương thức GetProperties, tôi có thể lưu vào bộ nhớ cache các PropertyDescriptors đã đọc để có hiệu suất nhưng khi thêm và xóa các cột trong thời gian chạy, tôi luôn muốn chúng được xây dựng lại
Bạn cũng sẽ nhận thấy trong phương thức GetProperties rằng Bộ mô tả thuộc tính được thêm vào cho các mục từ điển thuộc loại TestResultPropertyDescriptor. Đây là một lớp Bộ mô tả thuộc tính tùy chỉnh quản lý cách các thuộc tính được thiết lập và truy xuất. Hãy xem cách triển khai bên dưới:
public class TestResultPropertyDescriptor : PropertyDescriptor
{
#region Properties
public override Type ComponentType
{
get { return typeof(Dictionary<string, TestResultValue>); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
#endregion Properties
#region Constructor
public TestResultPropertyDescriptor(string key)
: base(key, null)
{
}
#endregion Constructor
#region Methods
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((Dictionary<string, TestResultValue>)component)[base.Name].Value;
}
public override void ResetValue(object component)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = string.Empty;
}
public override void SetValue(object component, object value)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = value.ToString();
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion Methods
}
Các thuộc tính chính cần xem xét trên lớp này là GetValue và SetValue. Tại đây, bạn có thể thấy thành phần đang được nhập dưới dạng từ điển và giá trị của khóa bên trong nó được Đặt hoặc được truy xuất. Điều quan trọng là từ điển trong lớp này phải cùng loại trong lớp Trình bao bọc hàng nếu không quá trình ép kiểu sẽ không thành công. Khi bộ mô tả được tạo, khóa (tên thuộc tính) được chuyển vào và được sử dụng để truy vấn từ điển để lấy giá trị chính xác.
Lấy từ blog của tôi tại:
Thực thi ICustomTypeDescriptor cho các thuộc tính động