在 UserControl 中公开 DataGridView 的 Columns 属性 并使其可通过 Designer 进行编辑
Expose Columns property of a DataGridView in UserControl and make it editable via Designer
Short description:
I have a UserControl with a DataGridView on it. I
want to expose the DataGridView Columns collection to the designer, so
I can change the columns on my User Control at design time.
问题:为此我需要哪些设计器属性?
对于那些对较长版本感兴趣的人:
我有一个具有以下功能的用户控件:
- 显示集合中 "pages" 项的 DataGridView。
- 用于 select 要显示哪个页面的 NumericUpdown 控件。
- 显示第一页/最后一页时将禁用的向上翻页/向下翻页按钮
- 显示项目的更改以视觉方式标记
- 用于保存/放弃更改的按钮。
此用户控件可以自主工作。它有一个函数供父控件使用:
- 显示页面(要显示的项目集合)
UserControl 引发两个事件:
- 活动页面已更改(带有页码)。应导致加载新页面
- 事件保存项目(包含已更改项目的集合)
我必须在多个窗体上显示此用户控件。唯一的区别是 DataGridViewColumn 的集合因表单而异。
我可以通过编程方式添加列,但使用设计器创建它们会更容易。
通常注册一个合适的UITypeEditor
using [Editor]
attribute. The editor which is used by the DataGridView
is DataGridViewColumnCollectionEditor
. But in this case, if we use this editor directly, the editor expect the the property belong to a DataGridView
and tries to convert value of ITypeDescriptorContext.Instance
到DataGridVeiew
就足够了,因为我们的编辑Columns
属性属于我们的用户控件,我们将收到一个异常:
Unable to cast object of type 'Type of Control'
to type
'System.Windows.Forms.DataGridView
'.
要解决这个问题,我们需要创建一个自定义UITypeEditor
并覆盖EditValue
并编辑Columns
属性的私有DataGridView
字段你的用户控制。
为此,我们创建了一个包含 DataGridView
的 ITypeDescriptorContext
实例,它是 Columns
属性 并将其传递给 EditValue
方法编辑。这样编辑器就会编辑我们的Columns
属性.
我们还使用 [DesignerSerializationVisibility]
属性装饰我们的 属性 以序列化集合内容。
这是实现。
MyUserControl
我假设您在设计时向用户控件添加一个 DataGridView
,它的名称将是 dataGridView1
。
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
[Editor(typeof(MyColumnEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public DataGridViewColumnCollection Columns
{
get { return this.dataGridView1.Columns; }
}
}
编辑器
public class MyColumnEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
var field = context.Instance.GetType().GetField("dataGridView1",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
var dataGridView1 = (DataGridView)field.GetValue(context.Instance);
dataGridView1.Site = ((Control)context.Instance).Site;
var columnsProperty = TypeDescriptor.GetProperties(dataGridView1)["Columns"];
var tdc = new TypeDescriptionContext(dataGridView1, columnsProperty);
var editor = (UITypeEditor)columnsProperty.GetEditor(typeof(UITypeEditor));
var result = editor.EditValue(tdc, provider, value);
dataGridView1.Site = null;
return result;
}
}
ITypeDescriptionContext 实现
public class TypeDescriptionContext : ITypeDescriptorContext
{
private Control editingObject;
private PropertyDescriptor editingProperty;
public TypeDescriptionContext(Control obj, PropertyDescriptor property)
{
editingObject = obj;
editingProperty = property;
}
public IContainer Container
{
get { return editingObject.Container; }
}
public object Instance
{
get { return editingObject; }
}
public void OnComponentChanged()
{
}
public bool OnComponentChanging()
{
return true;
}
public PropertyDescriptor PropertyDescriptor
{
get { return editingProperty; }
}
public object GetService(Type serviceType)
{
return editingObject.Site.GetService(serviceType);
}
}
Short description:
I have a UserControl with a DataGridView on it. I want to expose the DataGridView Columns collection to the designer, so I can change the columns on my User Control at design time.
问题:为此我需要哪些设计器属性?
对于那些对较长版本感兴趣的人:
我有一个具有以下功能的用户控件:
- 显示集合中 "pages" 项的 DataGridView。
- 用于 select 要显示哪个页面的 NumericUpdown 控件。
- 显示第一页/最后一页时将禁用的向上翻页/向下翻页按钮
- 显示项目的更改以视觉方式标记
- 用于保存/放弃更改的按钮。
此用户控件可以自主工作。它有一个函数供父控件使用:
- 显示页面(要显示的项目集合)
UserControl 引发两个事件:
- 活动页面已更改(带有页码)。应导致加载新页面
- 事件保存项目(包含已更改项目的集合)
我必须在多个窗体上显示此用户控件。唯一的区别是 DataGridViewColumn 的集合因表单而异。
我可以通过编程方式添加列,但使用设计器创建它们会更容易。
通常注册一个合适的UITypeEditor
using [Editor]
attribute. The editor which is used by the DataGridView
is DataGridViewColumnCollectionEditor
. But in this case, if we use this editor directly, the editor expect the the property belong to a DataGridView
and tries to convert value of ITypeDescriptorContext.Instance
到DataGridVeiew
就足够了,因为我们的编辑Columns
属性属于我们的用户控件,我们将收到一个异常:
Unable to cast object of type '
Type of Control'
to type 'System.Windows.Forms.DataGridView
'.
要解决这个问题,我们需要创建一个自定义UITypeEditor
并覆盖EditValue
并编辑Columns
属性的私有DataGridView
字段你的用户控制。
为此,我们创建了一个包含 DataGridView
的 ITypeDescriptorContext
实例,它是 Columns
属性 并将其传递给 EditValue
方法编辑。这样编辑器就会编辑我们的Columns
属性.
我们还使用 [DesignerSerializationVisibility]
属性装饰我们的 属性 以序列化集合内容。
这是实现。
MyUserControl
我假设您在设计时向用户控件添加一个 DataGridView
,它的名称将是 dataGridView1
。
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
[Editor(typeof(MyColumnEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public DataGridViewColumnCollection Columns
{
get { return this.dataGridView1.Columns; }
}
}
编辑器
public class MyColumnEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
var field = context.Instance.GetType().GetField("dataGridView1",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
var dataGridView1 = (DataGridView)field.GetValue(context.Instance);
dataGridView1.Site = ((Control)context.Instance).Site;
var columnsProperty = TypeDescriptor.GetProperties(dataGridView1)["Columns"];
var tdc = new TypeDescriptionContext(dataGridView1, columnsProperty);
var editor = (UITypeEditor)columnsProperty.GetEditor(typeof(UITypeEditor));
var result = editor.EditValue(tdc, provider, value);
dataGridView1.Site = null;
return result;
}
}
ITypeDescriptionContext 实现
public class TypeDescriptionContext : ITypeDescriptorContext
{
private Control editingObject;
private PropertyDescriptor editingProperty;
public TypeDescriptionContext(Control obj, PropertyDescriptor property)
{
editingObject = obj;
editingProperty = property;
}
public IContainer Container
{
get { return editingObject.Container; }
}
public object Instance
{
get { return editingObject; }
}
public void OnComponentChanged()
{
}
public bool OnComponentChanging()
{
return true;
}
public PropertyDescriptor PropertyDescriptor
{
get { return editingProperty; }
}
public object GetService(Type serviceType)
{
return editingObject.Site.GetService(serviceType);
}
}