零.引言

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

一.自定义类属性

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

  假如现在有一个类Line:

  

 public class Line
{
Point P1;
Point P2; public Point Point1
{
get{return P1;}
set{P1 = value;}
}
public Point Point2
{
get{return P2;}
set{P2 = value;}
}
public Line(Point point1, Point point2)
{
P1 = point1;
P2 = point2;
}
}

Line

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

  

 public class MyControl : System.Windows.Forms.UserControl
{
Line _line; public MyControl()
{
_line = new Line(new Point(, ),new Point(, ));
} public Line MyLine
{
get{return _line;}
set{_line = value;}
} protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.Red, this._line.Point1, this._line.Point2);
base.OnPaint(e);
}
}

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。

  

 public class LineConverter : TypeConverter
{
//该方法判断此类型可以转换为哪些类型
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
} if (destinationType == typeof(InstanceDescriptor))
{
return true;
} //调用基类方法处理其他情况
return base.CanConvertTo(context, destinationType);
}
//该方法判断哪些类型可以转换为此类型
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
} return base.CanConvertFrom(context, sourceType);
} // 将该类型转换为字符串和InstanceDescriptor
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value != null)
{
Line t = (Line)value;
string str = t.Point1.X + "," + t.Point1.Y + "," + t.Point2.X + "," + t.Point2.Y;
return str;
} if (destinationType == typeof(InstanceDescriptor) && value != null)
{
ConstructorInfo ci = typeof(TestTypeConverter.Line).GetConstructor(new Type[] { typeof(Point), typeof(Point) });
Line t = (Line)value;
return new InstanceDescriptor(ci, new object[] { t.Point1, t.Point2 });
} return base.ConvertTo(context, culture, value, destinationType);
}
//字符串和InstanceDescriptor转换为该类
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
string str = (string)value;
str = str.Trim();
string[] v = str.Split(',');
if (v.Length != )
{
throw new NotSupportedException("Invalid parameter format");
} int x1 = ;
int y1 = ;
int x2 = ;
int y2 = ;
bool res = int.TryParse(v[], out x1);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out y1);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out x2);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out y2);
if (res == false) throw new NotSupportedException("Invalid parameter format"); Line line = new Line(new Point(x1, y1), new Point(x2, y2));
return line;
} return base.ConvertFrom(context, culture, value);
}
}

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中重写这两个方法:

  

             public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
//return base.GetPropertiesSupported(context);
} public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return TypeDescriptor.GetProperties(value, attributes);
//return base.GetProperties(context, value, attributes);
}

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

  

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

五.属性的设计时串行化

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

  

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

  

 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Line MyLine
{
get
{
return _line;
}
set
{
_line = value;
}
}

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

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

  

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

六.总体的代码

  下面是完整的代码:

  

 using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Globalization;
using System.Reflection; namespace TestTypeConverter
{
//线条类
[TypeConverter(typeof(LineConverter))]
public class Line
{
// Line members.
Point P1;
Point P2; public Point Point1
{
get
{
return P1;
}
set
{
P1 = value;
}
} public Point Point2
{
get
{
return P2;
}
set
{
P2 = value;
}
} public Line(Point point1, Point point2)
{
P1 = point1;
P2 = point2;
}
} //转换器类
public class LineConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
} if (destinationType == typeof(InstanceDescriptor))
{
return true;
}
return base.CanConvertTo(context, destinationType);
} public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
} return base.CanConvertFrom(context, sourceType);
} public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value != null)
{
Line t = (Line)value;
string str = t.Point1.X + "," + t.Point1.Y + "," + t.Point2.X + "," + t.Point2.Y;
return str;
} if (destinationType == typeof(InstanceDescriptor))
{
ConstructorInfo ci = typeof(TestTypeConverter.Line).GetConstructor(new Type[] { typeof(Point), typeof(Point) });
Line t = (Line)value;
return new InstanceDescriptor(ci, new object[] { t.Point1, t.Point2 });
}
return base.ConvertTo(context, culture, value, destinationType);
} public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
string str = (string)value;
str = str.Trim();
string[] v = str.Split(',');
if (v.Length != )
{
throw new NotSupportedException("Invalid parameter format");
} int x1 = ;
int y1 = ;
int x2 = ;
int y2 = ;
bool res = int.TryParse(v[], out x1);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out y1);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out x2);
if (res == false) throw new NotSupportedException("Invalid parameter format");
res = int.TryParse(v[], out y2);
if (res == false) throw new NotSupportedException("Invalid parameter format"); Line line = new Line(new Point(x1, y1), new Point(x2, y2));
return line;
} return base.ConvertFrom(context, culture, value);
} public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
//return base.GetPropertiesSupported(context);
} public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return TypeDescriptor.GetProperties(value, attributes);
//return base.GetProperties(context, value, attributes);
}
} //控件类
public class MyControl : System.Windows.Forms.UserControl
{
Line _line; public MyControl()
{
_line = new TestTypeConverter.Line(
new Point(, ),
new Point(, )
);
} [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Line MyLine
{
get
{
return _line;
}
set
{
_line = value;
}
} protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.Red, this._line.Point1, this._line.Point2);
base.OnPaint(e);
}
}
}

整体代码

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

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

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

    零.引言 PropertyGrid中我们经常看到一些下拉式的编辑方式(Color属性)和弹出式编辑框(字体),这些都是为一些复杂的属性提供的编辑方式,本文主要说明如何实现这样的编辑方式. 一.为属性提 ...

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

    /* * 实习课上讲进阶功能所用文档,因为赶时间从网上抄抄改改,凑合能用,记录一下以备个人后用. * * ----------------------------------------------- ...

  3. Hibernate3提供的属性的延迟加载功能

    Hibernate3增强了对实体属性的延迟加载功能,要实现这个功能,分两个步骤 1.在hbm配置文件上对某个property设置lazy=true   <property name=" ...

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

    1,下面的样例是给表格UITableView添加编辑功能: (1)给表格添加长按功能,长按后表格进入编辑状态 (2)在编辑状态下,第一个分组处于删除状态,第二个分组处于插入状态 (3)点击删除图标,删 ...

  5. JAVAEE——BOS物流项目09:业务受理需求分析、创建表、实现自动分单、数据表格编辑功能使用方法和工作单快速录入

    1 学习计划 1.业务受理需求分析 n 业务通知单 n 工单 n 工作单 2.创建业务受理环节的数据表 n 业务通知单 n 工单 n 工作单 3.实现业务受理自动分单 n 在CRM服务端扩展方法根据手 ...

  6. Web界面开发必看!Kendo UI for jQuery编辑功能指南第二弹

    Kendo UI for jQuery最新试用版下载 Kendo UI目前最新提供Kendo UI for jQuery.Kendo UI for Angular.Kendo UI Support f ...

  7. 如何发挥Visual Studio 2019强大的编辑功能轻松编辑Keil项目

    本文地址:https://www.cnblogs.com/jqdy/p/12565161.html 习惯了VS的强大编辑功能,对Keil 5越来越深恶痛绝.查阅网络文章后按图索骥初步实现了VS编辑Ke ...

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

     通常我们去大保健的时候,都会找姑娘问一下这里能提供什么服务,什么价格,这时候可能姑娘会跟你口述一些服务或者提供一份服务清单,这样的话大 家就可以做到童嫂无欺,这样一份活生生的例子,在wcf中同样是一 ...

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

    Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitema ...

随机推荐

  1. Hard Process(二分)

    Hard Process Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submi ...

  2. vim中对文本的选择

    本文主要解说vim中对文本的选择,vim中选择文本分为: (1)选择字符  ----  命令行模式下输入小写v (2)选择行     ----  命令行模式下输入大写V (3)选择块     ---- ...

  3. 分类: LINUX apache 访问设置配置

    分类: LINUX 在一次面试的时候被问到apache访问控制的问题.由于以前对apache的访问控制都是通过iptalbes来实现的,没有实际在apache上操作过访问控制.所以只知道个大概: 在主 ...

  4. 多点触控插件Hammer.js

    插件描述:Hammer.js是一个开源的,轻量级的javascript库,它可以在不需要依赖其他东西的情况下识别触摸,鼠标事件. 使用方法: <script src=<span class ...

  5. RMAN增量备份-备份保留策略-设置备份集属性

    RMAN增量备份:增量备份级别:0,1,2,3,4BACKUP DATABASE也是创建数据库的完整备份,但是这种备份不同于增量备份的0级备份.这种备份不包含增量备份的0级备份,并不支持在此基础上再创 ...

  6. ADT "Running Android Lint" has encountered a problem

    解决办法: Window--->Preferences----->Android--------> LInt Error Checking----->when saving f ...

  7. iOS开发那些事儿(一)轮播器

    前言 市面上绝大部分的APP被打开之后映入眼帘的都是一个美轮美奂的轮播器,所以能做出一个符合需求.高效的轮播器成为了一个程序员的必备技能.所以今天的这篇博客就来谈谈轮播器这个看似简单的控件其中蕴含的道 ...

  8. tomcat日志分析详解

    在server.xml里的<host>标签下加上 <Valve className="org.apache.catalina.valves.AccessLogValve&q ...

  9. hdu 4612 Warm up(无向图Tarjan+树的直径)

    题意:有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求该桥树. 思路:这个标准想法很好想到,缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的 ...

  10. ckeditor的使用与验证

    1.使id=id的textArea变为富文本编辑框 function inittextarea(id) {  CKEDITOR.replace(id,{         width:'600px',  ...