PropertyGrid--为复杂属性提供编辑功能

零.引言

  PropertyGrid用来显示某一对象的属性,但是并不是所有的属性都能编辑,基本数据类型(int, double等)和.Net一些封装的类型(Size,Color等)可以编辑,但是对于自己定义的类型属性,是不能编辑的,本文主要讲述如何为自定义类型作为属性时,在PropertyGrid中进行编辑,以及进行设计时序列化,本文主要参考MSDN,错误和不足之处还望指正。

一.自定义类属性

  在PropertyGrid中能够编辑的都是.Net中自己封装的类,如果在一个类中有一个属性是我们自己定义的类型,在PropertyGrid中会是怎样的呢?看下面这个例子:

  假如现在有一个类Line:

  

 1 public class Line
 2 {
 3         Point P1;
 4         Point P2;
 5
 6         public Point Point1
 7         {
 8             get{return P1;}
 9             set{P1 = value;}
10         }
11         public Point Point2
12         {
13             get{return P2;}
14             set{P2 = value;}
15         }
16         public Line(Point point1, Point point2)
17         {
18             P1 = point1;
19             P2 = point2;
20          }
21 }

Line

  有一个控件类包含一个Line类型的属性:

  

 1 public class MyControl : System.Windows.Forms.UserControl
 2 {
 3         Line _line;
 4
 5         public MyControl()
 6         {
 7             _line = new Line(new Point(0, 0),new Point(100, 100));
 8         }
 9
10         public Line MyLine
11         {
12             get{return _line;}
13             set{_line = value;}
14         }
15
16         protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
17         {
18             e.Graphics.DrawLine(Pens.Red, this._line.Point1, this._line.Point2);
19             base.OnPaint(e);
20         }
21 }

MyControl

  重新生成,从工具箱中将该控件拖入Form中,查看他的属性,在PropertyGrid中显示如下:

  

  可见MyLine属性的值不显示,且是不能编辑的。这是因为PropertyGrid根本不知道Line是个什么类型,不知道要怎么显示,如果要其能在PropertyGrid中显示,必须给他提供转换器。

二.转换器概念

  PropertyGrid中属性的值都是以字符串的形式呈现给我们看的,显示一个对象的属性时,要将对象的属性值转换为字符串显示出来,而设置属性时,要将字符串转换为对象的属性值。这就需要一个转换器。在.Net中定义了一个TypeConverter 类,用来作为这些转换器的基类。.Net为一些类设计了专门的转换类,如:System.Drawing.ColorConverter ,System.Drawing.FontConverter等等,(具体参见MSDN中TypeConverter的继承关系)因此在PropertyGrid中能直接编辑这些属性。我们自己定义的类没有这样的类型转换器,因此在PropertyGrid中无法编辑,需要设计自己的转换器。

  先来看一下MSDN中对TypeConverter的描述:TypeConverter类提供一种将值的类型转换为其他类型以及访问标准值和子属性的统一方法。

  继承者说明:

  从 TypeConverter 继承,以实现您自己的转换要求。当从类继承时,可以重写以下方法:

  • 若要支持自定义类型转换,请重写 CanConvertFrom、CanConvertTo、ConvertFrom 和 ConvertTo 方法。
  • 若要转换必须重新创建对象才能更改其值的类型,请重写 CreateInstance 和 GetCreateInstanceSupported 方法。
  • 若要转换支持属性 (Property) 的类型,请重写 GetProperties 和 GetPropertiesSupported 方法。如果转换的类没有属性 (Property),而您需要实现属性 (Property),则可以将 TypeConverter.SimplePropertyDescriptor 类用作实现属性 (Property) 说明符的基。当从 TypeConverter.SimplePropertyDescriptor 继承时,必须重写 GetValue 和 SetValue 方法。
  • 若要转换支持标准值的类型,请重写 GetStandardValues、GetStandardValuesExclusive、GetStandardValuesSupported 和 IsValid 方法。

三.添加转换器

  好了,了解了基本原理后,我们来为Line添加转换器。这里新建一个LineConverter类,并继承于TypeConverter。

  

 1 public class LineConverter : TypeConverter
 2 {
 3         //该方法判断此类型可以转换为哪些类型
 4         public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
 5         {
 6             if (destinationType == typeof(string))
 7             {
 8                 return true;
 9             }
10
11             if (destinationType == typeof(InstanceDescriptor))
12             {
13                 return true;
14             }
15
16             //调用基类方法处理其他情况
17             return base.CanConvertTo(context, destinationType);
18         }
19 //该方法判断哪些类型可以转换为此类型
20         public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
21         {
22             if (sourceType == typeof(string))
23             {
24                 return true;
25             }
26
27             return base.CanConvertFrom(context, sourceType);
28         }
29
30         // 将该类型转换为字符串和InstanceDescriptor
31         public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
32         {
33             if (destinationType == typeof(string) && value != null)
34             {
35                 Line t = (Line)value;
36                 string str = t.Point1.X + "," + t.Point1.Y + "," + t.Point2.X + "," + t.Point2.Y;
37                 return str;
38             }
39
40             if (destinationType == typeof(InstanceDescriptor) && value != null)
41             {
42                 ConstructorInfo ci = typeof(TestTypeConverter.Line).GetConstructor(new Type[] { typeof(Point), typeof(Point) });
43                 Line t = (Line)value;
44                 return new InstanceDescriptor(ci, new object[] { t.Point1, t.Point2 });
45             }
46
47            return base.ConvertTo(context, culture, value, destinationType);
48         }
49         //字符串和InstanceDescriptor转换为该类
50         public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
51         {
52             if (value is string)
53             {
54                 string str = (string)value;
55                 str = str.Trim();
56                 string[] v = str.Split(‘,‘);
57                 if (v.Length != 4)
58                 {
59                     throw new NotSupportedException("Invalid parameter format");
60                 }
61
62                 int x1 = 0;
63                 int y1 = 0;
64                 int x2 = 0;
65                 int y2 = 0;
66                 bool res = int.TryParse(v[0], out x1);
67                 if (res == false) throw new NotSupportedException("Invalid parameter format");
68                 res = int.TryParse(v[1], out y1);
69                 if (res == false) throw new NotSupportedException("Invalid parameter format");
70                 res = int.TryParse(v[2], out x2);
71                 if (res == false) throw new NotSupportedException("Invalid parameter format");
72                 res = int.TryParse(v[3], out y2);
73                 if (res == false) throw new NotSupportedException("Invalid parameter format");
74
75                 Line line = new Line(new Point(x1, y1), new Point(x2, y2));
76                 return line;
77             }
78
79             return base.ConvertFrom(context, culture, value);
80         }
81 }

LineConverter

  在转换器类中,我们重写了四个函数,也就是在继承者说明中第一条的四个函数:

  ① CanConvertTo:用来说明此类可以转换为哪些类型,能转换就返回true,这里我们让他能转换为string和InstanceDescriptor,InstanceDescriptor是存储描述对象实例的信息,这些信息可用于创建对象的实例。一般转换都要实现这个转换,后面进行说明。

  ② CanConvertFrom:说明该类能有什么类型转换过来,能转换返回true,这里只对string类型进行转换。

  ③ ConvertTo:具体的转换实现,也就是提供方法将该类转换为在CanConvertTo中确定可以转换的类型。这里实现string和InstanceDescriptor的转换。

  ④ ConvertFrom:具体的转换实现,也就是提供方法将该类转换为在CanConvertFrom中确定可以转换的类型。这里实现string的转换。

  注意,每个方法处理完自己的转换后,依然要调用基类的函数来处理其他的情况。

  重写这四个方法后,给Line类型加上TypeConverter特性,在Line类定义的上面加上[TypeConverter(typeof(LineConverter))]。这样在需要转换的地方,就会调用我们所写的方法进行转换,Line属性在PropertyGrid中显示时,会调用ConvertTo,转换为字符串输出;设置属性时,会调用ConvertFrom将字符串转换为属性值,当然,字符串必须要有一定的格式,这就需要在转换的过程中进行判断,见ConvertFrom方法。

  重新生成,现在看MyControl中MyLine属性在PropertyGrid中的显示:

  

  可以看到MyLine属性显示出来了(以x1,y1,x2,y2的格式,我们在ConverTo方法中指定的),并可以编辑了,但必须是x1,y1,x2,y2的格式,否则会出现如下错误提示:

  

  这是我们在ConvertFrom方法中进行判断的。

四.编辑复杂属性的子属性

  现在可以编辑属性了,但是必须使用x1,y1,x2,y2固定的格式,不方便,而且容易出错,可不可以分别设置Line的两个点呢,可以,这里就需要编辑子属性了。Line中有Point1和Point2两个属性,我们让他们也显示出来。

  这里就是继承者说明中的第三条了。重写 GetProperties 和 GetPropertiesSupported 方法。在LineConverter中重写这两个方法:

  

 1             public override bool GetPropertiesSupported(ITypeDescriptorContext context)
 2             {
 3                 return true;
 4                 //return base.GetPropertiesSupported(context);
 5             }
 6
 7             public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
 8             {
 9                 return TypeDescriptor.GetProperties(value, attributes);
10                 //return base.GetProperties(context, value, attributes);
11             }
12         

  重新生成,现在看MyControl中MyLine属性在PropertyGrid中的显示:

  

  可以看到现在我们可以编辑MyLine的子属性了。

五.属性的设计时串行化

  最后说一说ConvertTo方法中为什么要实现InstanceDescriptor的转换。Visual Studio在我们编辑控件时,会在Form1.Designer.cs文件中自动的生成代码,设置控件的属性,这叫属性对设计时序列化,如下:

  

  我们定义的MyLine属性,在这里并没有,如何使它出现在这里呢,只需给MyLine属性加上DesignerSerializationVisibility特性。如下

  

 1 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
 2 public Line MyLine
 3 {
 4        get
 5        {
 6             return _line;
 7         }
 8         set
 9         {
10             _line = value;
11         }
12 }
13         

  这里DesignerSerializationVisibility.Visible表明代码生成器生成对象的代码。DesignerSerializationVisibility.Content说明该属性在编辑时要代码生成器产生对象内容的代码,而不是对象本身的代码。DesignerSerializationVisibility.Hide代码生成器不生成对象的代码。

  重新生成,并改变控件的位置,打开Form1.Designer.cs,会发现自动生成了MyLine属性的设置值。

  

  这是如何生成的呢,关键就在ConvertTo方法中实现InstanceDescriptor的转换,该方法告诉代码生成器,如何去构造一个Line类型,生成时,调用Line的构造函数构造新对象初始化MyLine属性值。

六.总体的代码

  下面是完整的代码:

  

  1 using System;
  2 using System.ComponentModel;
  3 using System.ComponentModel.Design.Serialization;
  4 using System.Drawing;
  5 using System.Globalization;
  6 using System.Reflection;
  7
  8 namespace TestTypeConverter
  9 {
 10     //线条类
 11     [TypeConverter(typeof(LineConverter))]
 12     public class Line
 13     {
 14         // Line members.
 15         Point P1;
 16         Point P2;
 17
 18         public Point Point1
 19         {
 20             get
 21             {
 22                 return P1;
 23             }
 24             set
 25             {
 26                 P1 = value;
 27             }
 28         }
 29
 30         public Point Point2
 31         {
 32             get
 33             {
 34                 return P2;
 35             }
 36             set
 37             {
 38                 P2 = value;
 39             }
 40         }
 41
 42         public Line(Point point1, Point point2)
 43         {
 44             P1 = point1;
 45             P2 = point2;
 46         }
 47     }
 48
 49     //转换器类
 50     public class LineConverter : TypeConverter
 51     {
 52          public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
 53         {
 54             if (destinationType == typeof(string))
 55             {
 56                 return true;
 57             }
 58
 59             if (destinationType == typeof(InstanceDescriptor))
 60             {
 61                 return true;
 62             }
 63             return base.CanConvertTo(context, destinationType);
 64         }
 65
 66         public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
 67         {
 68             if (sourceType == typeof(string))
 69             {
 70                 return true;
 71             }
 72
 73             return base.CanConvertFrom(context, sourceType);
 74         }
 75
 76         public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
 77         {
 78             if (destinationType == typeof(string) && value != null)
 79             {
 80                 Line t = (Line)value;
 81                 string str = t.Point1.X + "," + t.Point1.Y + "," + t.Point2.X + "," + t.Point2.Y;
 82                 return str;
 83             }
 84
 85             if (destinationType == typeof(InstanceDescriptor))
 86             {
 87                 ConstructorInfo ci = typeof(TestTypeConverter.Line).GetConstructor(new Type[] { typeof(Point), typeof(Point) });
 88                 Line t = (Line)value;
 89                 return new InstanceDescriptor(ci, new object[] { t.Point1, t.Point2 });
 90             }
 91             return base.ConvertTo(context, culture, value, destinationType);
 92         }
 93
 94         public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
 95         {
 96             if (value is string)
 97             {
 98                 string str = (string)value;
 99                 str = str.Trim();
100                 string[] v = str.Split(‘,‘);
101                 if (v.Length != 4)
102                 {
103                     throw new NotSupportedException("Invalid parameter format");
104                 }
105
106                 int x1 = 0;
107                 int y1 = 0;
108                 int x2 = 0;
109                 int y2 = 0;
110                 bool res = int.TryParse(v[0], out x1);
111                 if (res == false) throw new NotSupportedException("Invalid parameter format");
112                 res = int.TryParse(v[1], out y1);
113                 if (res == false) throw new NotSupportedException("Invalid parameter format");
114                 res = int.TryParse(v[2], out x2);
115                 if (res == false) throw new NotSupportedException("Invalid parameter format");
116                 res = int.TryParse(v[3], out y2);
117                 if (res == false) throw new NotSupportedException("Invalid parameter format");
118
119                 Line line = new Line(new Point(x1, y1), new Point(x2, y2));
120                 return line;
121             }
122
123             return base.ConvertFrom(context, culture, value);
124         }
125
126
127         public override bool GetPropertiesSupported(ITypeDescriptorContext context)
128         {
129             return true;
130             //return base.GetPropertiesSupported(context);
131         }
132
133         public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
134         {
135             return TypeDescriptor.GetProperties(value, attributes);
136             //return base.GetProperties(context, value, attributes);
137         }
138     }
139
140     //控件类
141     public class MyControl : System.Windows.Forms.UserControl
142     {
143         Line _line;
144
145         public MyControl()
146         {
147             _line = new TestTypeConverter.Line(
148                 new Point(0, 0),
149                 new Point(100, 100)
150                 );
151         }
152
153         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
154         public Line MyLine
155         {
156             get
157             {
158                 return _line;
159             }
160             set
161             {
162                 _line = value;
163             }
164         }
165
166         protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
167         {
168             e.Graphics.DrawLine(Pens.Red, this._line.Point1, this._line.Point2);
169             base.OnPaint(e);
170         }
171     }
172 }

整体代码

  新建一个Windows工程,添加该文件,在工具箱中找到我们的MyControl控件,拖入Form中,在属性框中查看控件的属性。

时间: 2024-10-17 08:08:57

PropertyGrid--为复杂属性提供编辑功能的相关文章

PropertyGrid—为复杂属性提供下拉式编辑框和弹出式编辑框

零.引言 PropertyGrid中我们经常看到一些下拉式的编辑方式(Color属性)和弹出式编辑框(字体),这些都是为一些复杂的属性提供的编辑方式,本文主要说明如何实现这样的编辑方式. 一.为属性提供编辑类 弹出式和下拉式是如何实现的呢,这需要为属性提供一个专门的编辑类..Net为我们提供了一个System.Drawing.Design.UITypeEditor类,它是所有编辑类的基类,从他继承出了诸如ColorEditor.FontEditor的类,因此我们可以在属性框中编辑颜色和字体.定义

AE二次开发中几个功能速成归纳(符号设计器、创建要素、图形编辑、属性表编辑、缓冲区分析)

/* * 实习课上讲进阶功能所用文档,因为赶时间从网上抄抄改改,凑合能用,记录一下以备个人后用. * * ------------------------------------------------------------------- * * 使用前提:已搭建好AE的GIS基本框架,包括TOC.mapcontrol.toolbar拖控件,mxd.shp文件载入显示,查看图层属性表等 * * --------------------------------------------------

Swift - 给表格添加编辑功能(删除,插入)

1,下面的样例是给表格UITableView添加编辑功能: (1)给表格添加长按功能,长按后表格进入编辑状态 (2)在编辑状态下,第一个分组处于删除状态,第二个分组处于插入状态 (3)点击删除图标,删除对应条目 (4)点击添加图标,插入一条新数据 2,效果图           3,代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 3

flex中dataGrid的编辑功能

flex中dataGrid的编辑功能 在flex的dataGrid中,很多情况下列是需要嵌入其他的控制的,比如:checkbox,combox等,此时需要利用dataGrid的如下功能: 1.datagrid编辑功能简介     当我们点击datagrid中的一个单元格进行编辑时,可结合使用一些组件,RadioButtonGroup.checkbox.ComboBox等     datagrid的编辑功能必须使用以下组件:editorDataField.itemEditor和itemRender

Dynamics 365 for CRM: Sitemap站点图的可视化编辑功能

Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitemap编辑工具进行编辑(非常方便). 在Dynamics 365 for CRM 中原生地提供了站点图的可视化编辑功能,非常强大方便: 1.在默认解决方案或自定义解决方案的"客户端扩展"中,找到"站点地图"记录:如果没有则需要点击"添加现成",添加&qu

MVVM架构~使用boxy和knockoutjs实现编辑功能

这个功能我认为非常有用,尤其在后台管理系统中,它对用户来说,使用体验这块非常不错,下面是它的截图 说在前 在实现这个功能中,我们对knockout的基础知识一定要牢牢掌握,要知道,在knockout里一切都是对象,并且要知识knockout可以根据DIV进行绑定,对于验证这块,可以根据你的object进行验证,而并非只能验证全局对象,而对于boxy来说, 要知道它加载HTML代码的方式(如果HTML代码上有knockout绑定,则需要先将html代码加载上,然后再绑定model,这就用到了box

十五天精通WCF——第三天 client如何知道server提供的功能清单

 通常我们去大保健的时候,都会找姑娘问一下这里能提供什么服务,什么价格,这时候可能姑娘会跟你口述一些服务或者提供一份服务清单,这样的话大 家就可以做到童嫂无欺,这样一份活生生的例子,在wcf中同样是一个道理,只有client了解service能提供哪些功能,client才可以根据server提供的功能进行 消费,那问题来了,service怎么把功能提供给client进行选择呢???这个就是我这一篇要聊的wsdl(web service description language)... 一:wsd

034 datagrid编辑功能使用方式 - bos

数据表格编辑功能是以列为单位通过数据表格中的列属性指定具体那一列具有编辑功能:editor beginEdit:开始编辑一行 endEdit:编辑一行结束 insertRow:插入一行 deleteRow:删除一行 getRowIndex:给定行对象,返回其在数据表格中的索引 数据表格用于监听结束编辑的事件: onAfterEdit:编辑完后触发 onAfterEdit:function(index, data, changes){ alert(data.name); } 示例代码 <table

jquery-easyui 中表格的行编辑功能

具体实现代码如下: <table id="tt"></table> $('#tt').datagrid({ title:'Editable DataGrid', iconCls:'icon-edit', width:660, height:250, singleSelect:true, idField:'itemid', url:'datagrid_data.json', columns:[[ {field:'itemid',title:'Item ID',wi