上一篇讲了如何通过TypeConverter来让自定义的类型的属性在属性窗口正确的显示 这一篇介绍为属性添加一个对话框或者下拉框来编辑属性 大家都应该见过这样东西
左边是控件的Font属性 当属性被选中的时候 右边会出现一个按钮 然后点击按钮会出现一个对话框来设置字体的一些参数
而右边的是控件的某个Color属性 属性被选中时 右边出现一个下拉箭头的按钮 然后点击出现一个下拉菜单来选择然后
这两个东西 自己也可以做一个 继续用前面的NumberRange属性来说事、、
上一篇为NumberRange结构写了一个TypeConverter类来完成属性窗口的转换 同样 要实现用对话框或者下来框来编辑属性 同样要写一个类 - Editor:
[Editor(typeof(NumberRangeEditor),typeof(UITypeEditor))] [TypeConverter(typeof(NumberRangeConverter))] public struct NumberRange { //同上篇、、、 }NumberRange还是和以前的一样 这次可以看到 在上面又多了一个特性 添加了一个Editor当然 得写一个NumberRangeEditor类:
public class NumberRangeEditor : UITypeEditor { //Editor }继承UITypeEditor 里面需要重写两个方法
先说说使用对话框的模式 首先得有一个对话框来编辑属性:
就像平常写代码一样 先搞一个对话框出来用于编辑NumberRange属性
窗体代码:
public partial class FrmEdit : Form { public FrmEdit(NumberRange nr) { InitializeComponent(); this._NumberRange = nr;//接收一个值 用于绑定显示 this.StartPosition = FormStartPosition.CenterParent; this.btn_cancel.Click += (s, e) => this.Close(); } private NumberRange _NumberRange; public NumberRange NumberRange { get { return _NumberRange; } set { _NumberRange = value; } } //绑定显示 private void FrmEdit_Load(object sender, EventArgs e) { tbx_low.Text = this._NumberRange.Low.ToString(); tbx_high.Text = this._NumberRange.High.ToString(); } private void btn_ok_Click(object sender, EventArgs e) { this._NumberRange.Low = int.Parse(tbx_low.Text);//没有验证数据有效性 this._NumberRange.High = int.Parse(tbx_high.Text); this.DialogResult = DialogResult.OK;//模态对话框对DialogResult赋值的时候 会自动关闭窗体 } }窗体的代码 就不做多余的解释了和平常写程序没多大区别根据个人习惯 反正达到从你的窗体上可以得到一个新的属性的值就可以了
现在窗体有了 怎么把它搞到属性窗口上面去? 刚才上面说了 需要从写两个方法 那么现在先来重写第一个:
public override UITypeEditorEditStyle GetEditStyle( System.ComponentModel.ITypeDescriptorContext context) { if (context != null && context.Instance != null) { return UITypeEditorEditStyle.Modal;//表示模态对话框模式 } return base.GetEditStyle(context); }
对于context判断一下他是否为空就行了 他可以用来获取正在编辑的属性的信息(- -!、、至于具体是啥信息 我也没有深入研究过 书上说"如:正在使用同一个编辑器类来控制不同类型的不同属性的编辑" 但是我知道的是 一个编辑器类可以被其他类也使用 比如集合之类的属性 vs自带都有一个公共的编辑器 而你属性中 要是有集合的话 默认就会跳出集合的编辑器 好了不多说 反正就这样了)
然后来看第二个关键的:
public override object EditValue( System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value) { if (context != null && provider != null) { IWindowsFormsEditorService edSvc = null; edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); if (edSvc != null) { NumberTextBox ntbx = (NumberTextBox)context.Instance; FrmEdit frmEdit = new FrmEdit(ntbx.NumberRange);//创建对话框 if (edSvc.ShowDialog(frmEdit) == System.Windows.Forms.DialogResult.OK) { value = frmEdit.NumberRange; return value;//返回新的值 } } } return base.EditValue(context, provider, value); }context.Instance是控件本身的实例 通过它可以获取到控件 然后接下来创建一个对话框 通过方法的第二个参数的GetService获取到的调用对话框的一个引用去调用对话框(其实具体啥意思我也不太明白 书上感觉说的很模糊一句话就带过了 就目前而言我也只知道要这么去用 虽然我试过直接忽略掉那个edSvc然后直接frmEdit.ShowDialog()也能正常的完成功能)因为我在窗体的btn_ok里面返回的DialogResult是OK所以 如果判断到是ok就证明点击了确定按钮 然后返回对话框编辑后的值、、
这样就能实现一个对话框来编辑属性了:
效果如上 还有对话框要怎么设计是自己的事情 上面的我忘了去掉最大化 最小化什么还有验证数据有效性什么的、、、自己做的时候自己处理就行了
然后接下来说一下通过下拉的方式来稿 那么下拉的时候就不是创建窗体了 这个时候需要一个自定义的用户控件
而这个自定义控件将作为下拉框显示在属性的下面当点击的时候 代码如下:
[ToolboxItem(false)]//不要在工具栏上面显示 public partial class EditDropDown : UserControl { public EditDropDown(NumberRange nr) { InitializeComponent(); this._NumberRange = nr; } private NumberRange _NumberRange; public NumberRange NumberRange { get { return _NumberRange; } set { _NumberRange = value; } } private bool m_bCancel; //绑定显示 protected override void OnLoad(EventArgs e) { tbx_low.Text = this._NumberRange.Low.ToString(); tbx_high.Text = this._NumberRange.High.ToString(); base.OnLoad(e); } //如果用户点下esc protected override bool ProcessDialogKey(Keys keyData) { m_bCancel = keyData == Keys.Escape; return base.ProcessDialogKey(keyData); } //当用 回车 esc 鼠标点击控件以外的区域的时候 控件会消失 protected override void OnLeave(EventArgs e) { if (!m_bCancel) {//如果用户是点击的esc就不用赋值了 数据没有验证 this._NumberRange.Low = int.Parse(tbx_low.Text); this._NumberRange.High = int.Parse(tbx_high.Text); } base.OnLeave(e); } }注意上面的ToolboxItem表示不在工具栏显示此控件 不然到时候你误点了控件添加到窗体上是会报错的(当然也就一个提示框) 因为构造器需要接收一个参数 这个控件本来就是用来修改控件属性的 单独的存在是没有意义的 而且如果以后是要写一个控件库的话 这些无关紧要的东西 尽量都用internal关键字修饰 不然到时候别人拿着你的dll发现里面一堆不知道是啥的东西、、
还有需要注意的是 下来框不像对话框那样 还有一个按钮来确定输入的值 下拉框没有按钮的(当然 你要放一个也可以) 一般情况下到时候点击属性右边的拉下箭头的时候 会出现下拉框 如果在上面敲击回车 esc 或者点击其他区域的时候 下拉框就自动消失 然后通常情况下 下拉框消失 也就意味着赋值结束 所以从设计上也通常这样设计 不过要排除esc的情况 所以上面控件里面才有判断的
下拉框和对话框的Editor的代码几乎一模一样 没多大区别 来看看代码你就知道:
public override UITypeEditorEditStyle GetEditStyle( System.ComponentModel.ITypeDescriptorContext context) { if (context != null && context.Instance != null) { return UITypeEditorEditStyle.DropDown;//表示下拉框模式 } return base.GetEditStyle(context); } public override object EditValue( System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value) { if (context != null && provider != null) { IWindowsFormsEditorService edSvc = null; edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); if (edSvc != null) { NumberTextBox ntbx = (NumberTextBox)context.Instance; EditDropDown dropDown = new EditDropDown(ntbx.NumberRange); edSvc.DropDownControl(dropDown);//显示下拉框 return dropDown.NumberRange; } } return base.EditValue(context, provider, value); }而在上面也只是把创建对话框的代码换成了创建自定义控件的代码而已 然后调用edSvc.DropDownControl()来显示下拉框 等下拉框消失后去取出自定义控件里面的值返回 效果如下:
这一篇也差不多了到此结束、、