一:背景介绍
TypeConverter 类是.NET Framework 中的一个重要组件,用于在不同类型之间进行转换。它位于System.ComponentModel命名空间中,是.NET框架中的类型转换机制的核心之一。
在软件开发中,经常需要处理不同类型之间的转换,比如将字符串转换为整数、将对象转换为字符串等。TypeConverter 类的出现正是为了解决这类问题。它提供了一种灵活的机制,允许开发人员自定义类型之间的转换规则,从而实现更加灵活和可扩展的类型转换功能。
适用场景:
-
Windows Forms 开发: 在 Windows Forms 应用程序中,TypeConverter 类常用于属性窗格(PropertyGrid)的实现中。通过为自定义对象提供自定义的 TypeConverter,可以让属性窗格正确地显示和编辑对象的属性,提高用户体验。
-
数据绑定: 在数据绑定的场景中,TypeConverter 用于将数据从一种类型转换为另一种类型,以便正确地绑定到用户界面控件上。通过为数据模型的属性提供合适的 TypeConverter,可以实现数据在界面上的正确展示和编辑。
-
配置文件处理: 在处理配置文件或者用户设置时,TypeConverter 可以帮助将配置数据从字符串形式转换为实际的对象类型。这样可以简化配置文件的处理,并提供更加友好的配置方式。
-
序列化与反序列化: 在对象的序列化与反序列化过程中,TypeConverter 也扮演着重要的角色。它可以帮助将对象转换为字符串形式进行序列化,或者从字符串形式反序列化为对象,从而实现对象的持久化存储和恢复。
二:开发过程中遇到的问题
在c#联合halcon开发中我经常需要操作Halcon的一个数据类型Htuple。
但是在开发过程中不停的对Htuple进行手动书写代码我觉得真是太麻烦了,你需要不停的
Htuple ht;然后ht.D,ht.L,ht.I 等操作这样会很麻烦的,想想都觉得头疼
那么有没有一种办法就是这样一个场景,我需要进行数据类型的转换,但是我在一个类中定义的一个属性的数据类型,我需要转换成各种情况下我真实的需要的数据类型呢?
答案是有的,使用微软中提供的TypeConverter进行数据转换,但是可以看到在网络上大部分进行数据转换只能够转为一种,适应性,可扩展性,都不能够满足人们的需求。
看下面这段代码。
//重要参数创建模型的模型ID
private HTuple _modelID;
[Browsable(true),
Category("输出:模型参数设置"),
DisplayName("模板ID"),
Description("创建模板生成的模板ID")
, TypeConverter(typeof(HTupleTypeConverter<int>))
]
public HTuple ModelID
{
get { return _modelID; }
set
{
_modelID = value;
OnPropertyChanged("ModelID");
}
}
我们可以观察到我希望的是直接对属性定义为Htuple类型,然后通过特性的方式直接转为我任何期望的数据类型。c#中的int,float,double,string等。
三:对PropertyGrid控件的使用进行一个汇总。
顾名思义,叫做属性网格控件
通过PropertyGrid中的SelectObj进行对象绑定,例如
propG_参数设置.SelectedObject = modelMatching.modelParemeter;
通过这段代码就可以完成实体类中的属性和属性网格控件(PropertyGrid)进行的一种映射,
常用特性:我简要写一下,网上有很多讲解的不过多赘述
//重要参数创建模型的模型ID
private HTuple _modelID;
[Browsable(true),
Category("输出:模型参数设置"),
DisplayName("模板ID"),
Description("创建模板生成的模板ID")
, TypeConverter(typeof(HTupleTypeConverter<int>))
]
Browsable:当前属性在外部是否可见,一般不见也可以默认的就是true,允许可见的
Category:网格中多个属性的时候需要对网格中的属性进行归类,假设:写上输入
那么属于输入的就在哪里排列着,输出也一样。
其实相当于Halcon中的输入与输出对不同的属性进行了一个归类。(我说的是大白话,需要定义的可以查找网上其他资源)
DisplayName:就是在属性网格空间中每个属性对应的名字:例如这里的margin,Tag等
Description:对每一个属性的描述,看到了吗就是下面这种的说明
个人认为有这4个特性就可以了。
四:对上述第二个问题解决方案的源码
public class HTupleTypeConverter<T> : TypeConverter
{
// 指示是否可以将值转换为指定的目标类型
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return true;
}
/// <summary>
/// 当用户在后端代码改变的时候执行这里
/// HTuple 类型的值转换为其他类型,以满足不同的显示或存储需求。
/// </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 is HTuple)
{
//如果当前的value类型是Htuple类型那么
string str = value.ToString();
var value1= ConvertFromString(str);
return base.ConvertTo(context, culture, value1, destinationType);
}
if (destinationType == typeof(string) && value is T)
{
T val = (T)value;
// 根据泛型类型 T 将 val 转换为字符串
return val.ToString();
}
else if (destinationType == typeof(HTuple) && value is T)
{
T val = (T)value;
// 根据泛型类型 T 转换为 HTuple
// 这里需要你根据实际情况来实现转换
HTuple convertedValue = ConvertToHTuple(val);
return convertedValue;
}
return base.ConvertTo(context, culture, value, destinationType);
}
// 指示是否可以将从字符串转换的值分配给对象
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string) || sourceType == typeof(T))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
/// <summary>
/// 当用户手动在控件中输入数据的时候会执行这里的代码
///外部数据源将数据转换为 HTuple 类型
/// </summary>
/// <param name="context"></param>
/// <param name="culture"></param>
/// <param name="value"></param>
/// <returns></returns>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
// 根据字符串创建泛型类型 T 对象
string stringValue = (string)value;
// 根据泛型类型 T 创建对应类型的对象
T convertedValue = ConvertFromString(stringValue);
if (convertedValue is T)
{
// 将泛型类型 T 转换为 HTuple
// 这里需要你根据实际情况来实现转换
HTuple toHtuple = ConvertToHTuple(convertedValue);
return toHtuple;
}
}
else if (value is T)
{
// 将泛型类型 T 转换为 HTuple
T val = (T)value;
// 这里需要你根据实际情况来实现转换
HTuple convertedValue = ConvertToHTuple(val);
return convertedValue;
}
return base.ConvertFrom(context, culture, value);
}
// 根据泛型类型 T 将其转换为 HTuple
private HTuple ConvertToHTuple(T value)
{
// 这里需要你根据实际情况来实现转换
// 这里仅作为示例,你需要根据实际情况来实现转换
// 示例中将 T 类型的值转换为 HTuple 类型
return new HTuple(value);
}
// 根据字符串创建泛型类型 T 对象
private T ConvertFromString(string value)
{
//获取当前属性需要转为的转换为的数据类型
Type aType = typeof(T);
object obj=Convert.ChangeType(value, typeof(T));
return (T)obj;
}
}
可以观察到有两个重要的重写TypeConveter的方法一个是 ConvertTo和ConvertFrom,
我用大白话的形式像大家解释这两个方法的区别。
ConvertTo就是当你手动在后端,对当前属性进行赋值的时候,想要刷新到绑定到PropertyGrid控件的时候执行这里(注意:为什么需要这样转换,因为PropertyGrid控件不认识你定义的Htuple类型,所以你在后端每次赋值的时候都需要转为c#的数据类型)
假如你不把Htuple转为PropertyGrid控件可以接受的数据类型,显然最后(可能在PropertyGrid控件中可能不会显示属性,或者也不会更改属性)
ConvertFrom就是当你更改PropertyGrid控件中的代码的时候,会从UI层,转为当前你定义的Htuple数据类型(注意:我的初衷就是不要对Htuple进行数据转换,通过这样我就可以做到了)
显然这个泛型类是可以改变的不单是从Htuple转为泛型数据类型T。
private HTuple ConvertToHTuple(T value)
{
// 这里需要你根据实际情况来实现转换
// 这里仅作为示例,你需要根据实际情况来实现转换
// 示例中将 T 类型的值转换为 HTuple 类型
return new HTuple(value);
}
你可以修改这里的逻辑代码,当PropertyGrid控件(UI层更改数据后)转换为实际的当前属性的数据类型。大家可以根据自己的需求来改变。(提供一个思路:假如当前属性的数据类型不是任何的c#数据类型是你定义的类的情况下,你是不是就需要这样来改写了。)
五:在PropertyGrid控件中对数据更新的问题。
前提:你需要用TypeConverter进行数据转换,必须要测试一下,ConvertTo,ConvetFrom这两个重写的方法,要保证当后端变换到UI(ConvertTo方法可以执行,可以到达你预想的数据类型),UI到后端属性(ConvetFrom方法也可以转换为你自己当前定义的属性的数据类型)。
前提不出错,当你调用下面的Refresh()方法才不会出现错误,不然你肯定会遇到一些错误:
1:需要手动点击PropertyGrid控件中的属性,才会显示数据
2:转换错误后:在propertygrid控件中是空白
当你后端代码变化后,你想让PropertyGrid中的数据也显示变化,应该怎么办呢,答案是
调用ProPertyGrid控件中的。控件.Refresh()方法来刷新数据,这样就可以刷新到网格属性控件上供用户使用了。
六:结束
在本文中,我们深入了解了微软中的 C# 中的 TypeConverter 类以及PropertyGrid控件,它作为 .NET Framework 中的重要组件,在不同类型之间的转换中发挥着关键作用。通过灵活的机制,TypeConverter 类使得类型转换更加容易和可扩展,为开发人员提供了丰富的选择和解决方案。
不论是在 Windows Forms 开发中,数据绑定,配置文件处理,还是序列化与反序列化过程中,TypeConverter 类都能展现其强大的功能和应用价值。它不仅简化了开发工作,提高了开发效率,还提升了用户体验,使得软件更加灵活和易于维护。
在未来的软件开发中,我们可以继续深入研究和应用 TypeConverter 类,发掘其更多的潜力,为构建高效、可靠的软件系统做出更大的贡献。
如有错误,希望大家指证,博主愿意和大家一起学习,一起进步,觉得不错的希望您能关注博主,我将持续分享开发中遇到的问题。