PropertyGrid使用总结2 TypeConverter

类型转换的作用,是实现PropertyGrid输入的多个文本信息,能够与对象进行有效的转化,比如我们具有如下一个对象:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
 
namespace AlbertControlExample.Controls 
{ 
/// <summary>
/// 定义一个新控件
/// </summary>
public class AlbertControlDef : Control 
{ 
public OffsetDef offsetDef 
{ 
 
get; set; 
} = new OffsetDef(0,0); 
public AlbertControlDef() 
{ 
} 
 
protected override void OnPaint(PaintEventArgs e) 
{ 
base.OnPaint(e); 
} 
} 
 
/// <summary>
/// 定义一个左边偏移
/// </summary>
public class OffsetDef{ 
 
public OffsetDef(int left, int top) 
{ 
this.Left = left; 
this.Top = top; 
} 
 
public double Left { get; set; } 
public double Top { get; set; } 
} 
} 

我们看一下显示当前的控件,会发现OffsetDef并不会显示属性,且无法编辑,如图:

这是由于系统并无法解析OffsetDef对象,意思无法将它转化为可以描述的文本集合,就不能对当前对象进行描述,那我们就需要利用TypeConverter对象,其可以定义如下:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
 
namespace AlbertControlExample.Controls 
{ 
/// <summary>
/// 定义一个新控件
/// </summary>
public class AlbertControlDef : Control 
{ 
[TypeConverter(typeof(OffsetConverterDef))] 
public OffsetDef offsetDef 
{ 
get; set; 
} = new OffsetDef(100,100); 
public AlbertControlDef() 
{ 
} 
 
protected override void OnPaint(PaintEventArgs e) 
{ 
base.OnPaint(e); 
} 
} 
 
/// <summary>
/// 定义一个左边偏移
/// </summary>
public class OffsetDef{ 
 
public OffsetDef(int left, int top) 
{ 
this.Left = left; 
this.Top = top; 
} 
 
public double Left { get; set; } 
public double Top { get; set; } 
} 
 
public class OffsetConverterDef : TypeConverter { 
 
/// <summary>
/// 是否支持属性显示
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override bool GetPropertiesSupported(ITypeDescriptorContext context) 
{ 
return true; 
} 
 
/// <summary>
/// 返回属性文本的集合定义
/// </summary>
/// <param name="context"></param>
/// <param name="value"></param>
/// <param name="attributes"></param>
/// <returns></returns>
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) 
{ 
var properties= TypeDescriptor.GetProperties(value); 
return properties; 
} 
} 
} 

通过返回属性集合,系统会默认显示属性到窗体,其显示结果如下:

TypeDescriptor肯定不止这么简单,其有几个重要的函数,可以实现对象和输入框之间的互相转换,下面分别说明集合函数的功能和作用

/// <summary>
/// 能否将对象转换为字符串
/// </summary>
/// <param name="context"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
{ 
return true; 
} 
 
/// <summary>
/// 主要把对象转化为指定的字符串
/// </summary>
/// <param name="context"></param>
/// <param name="culture"></param>
/// <param name="value"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 
{ 
if (value.GetType() == typeof(OffsetDef)) 
{ 
OffsetDef offsetDef = value as OffsetDef; 
return string.Format("{0},{1}",offsetDef.Left, offsetDef.Top); 
 
} 
return base.ConvertTo(context, culture, value, destinationType); 
} 

其显示会如下:

当前定义是将对象转化为字符串对象,并且显示在当前对象对应的文本框之中。以上界面,我们通过修改100,100这个文本框是无法修改的,它只能转换过来,假如我们想可以编辑他,并且自动转换为对象,可以实现如下几个函数:

/// <summary>
/// 是否能从文本转化为对象
/// </summary>
/// <param name="context"></param>
/// <param name="sourceType"></param>
/// <returns></returns>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 
{ 
return true; 
} 
 
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 
{ 
if (value.GetType() == typeof(string)) { 
string objDec = value.ToString(); 
 
string[] vsp = objDec.Split(','); 
 
if (vsp.Length == 2) { 
 
OffsetDef offsetDef = new OffsetDef(int.Parse(vsp[0]),int.Parse(vsp[1])); 
return offsetDef; 
} 
 
} 
return base.ConvertFrom(context, culture, value); 
 
 
} 

通过以上的定义,我们发现,现在修改100,100这个文本框的内容,对象的属性定义会自动改变,那是因为只要修改对象所对应的文本框,就会调用ConvertFrom函数,将当前文本框的内容自动转化为对象,但是不能输入错误的值,比如输入一个无法转换为int的字符串,那么就会报错。其结果显示如下:

但是其发现另外一个问题,我们修改100,100为100,200是否,那么对应的left和top的值会自动变化,但是我们修改Left/top的值的时候,并没有影响到offsetDef的值,那是因为修改left\top不会触发ConvertFrom函数,所以以上的转化就会出问题,那我们怎么解决这个问题呢,则需要实现TypeDescriptor另外两个函数:

/// <summary>
/// 是否能重新创建对象,默认是不创建,当前是需要创建的,所以我们直接返回true
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) 
{ 
return true; 
} 
 
/// <summary>
/// 返回创建的实例对象,这个函数的调用,只要当前属性列表发生变化,当前函数都会启动调用,也会返回当前所有的属性列表
/// </summary>
/// <param name="context"></param>
/// <param name="propertyValues"></param>
/// <returns></returns>
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) 
{ 
if (propertyValues.Count == 2) { 
 
OffsetDef def = new OffsetDef(int.Parse(propertyValues["Left"].ToString()), int.Parse(propertyValues["Top"].ToString())); 
return def; 
} 
return base.CreateInstance(context, propertyValues); 
} 

通过以上两个函数,你会发现,修改任何属性对应的文本,那么这个对象就会重新定义,对象也会跟着改变。这个对象不仅仅有这些功能,其还有几个非常重要的函数。

public override bool GetStandardValuesSupported(ITypeDescriptorContext context) 
{ 
return true; 
} 
 
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) 
{ 
StandardValuesCollection standardValues = new StandardValuesCollection(new string[]{ "100,200","200,300","400,500"}); 
return standardValues; 
} 

 

通过以上函数,可以对当前的对象指定一个标准的值,供用户下拉选择,通过以上的代码,则可以实现如下功能:

同时还有其他几个函数,这里就不一一说明,通过TypeConverter的定义,我们可以实现属性文本列表和对象的互相转换,实现对象的可配置。当前对象还有一个很重要的特性没有说明,就是attributes和CultureInfo,ITypeDescriptorContext三个对象:

  1. attributes对象当然是对每个Property属性上的attribute获取和进行访问的列表
  2. CultureInfo 主要用于实现控件的国际化的定义
  3. ITypeDescriptorContext 主要是当前类型标识的上下文信息,当前类型定义并没有指定TypeDescriptor对象,所以当前对象为空,我们在接下来的章节,会介绍此对象。
posted @ 2020-02-11 22:17  Min.Xiaoshuang  阅读(1737)  评论(0编辑  收藏  举报