新项目开发涉及到了窗体控件的使用。其中,尤以propertyGrid为重。被众多先辈誉为RAD开发的重要工具。Rapid application development (RAD) is a term originally used to describe a software development process introduced by James Martin in 1991. Martin's methodology involves iterative development and the construction of prototypes。以上英文简介来源于http://en.wikipedia.org/wiki/Rapid_application_development
本次开发到目前为止,涉及到了以下问题:
1、信息的本地化
2、属性类的动态隐藏
3、属性编辑器的定制
4、属性排序方式
5、属性值的验证
6、自定义类型复合属性的表示
针对以上问题,接下来讨论其解决方案。
1、描述信息的本地化
首先、信息本地化的实现是通过system.ComponentModel名空间下的属性类(Attribute 类)来实现。本类皆以Attribute结尾,提供了属性的描述[Description(String s)]、类别[Category(String s)]、显示名称[DisplayName(String s)]、可写性[ReadOnly(bool bo)]、显示性[Browsable(bool bo)]。以属性描述为例示例如下:
private int a;//a不是传入propertyGrid控件的属性,其get/set方法名才是默认属性名。
[Description(descriptString)]//descriptString 将描述信息传给控件作为属性A的描述,通过继承该类
public int A{} //可以将string型参数descriptString作为key,根据系统语言环境参数进行``````````````````````````````````//查值,以实现本地化。
.net中所有基本类型都有相对应的编辑器,这导致Enum类型的属性值显示的是内部enum值,并非用户容易理解的信息,对这一类型的本地化工作相对复杂。由于enum下的成员不是作为属性显示所以他的[DisplayName(String s)]无用武之地。而信息的表示是由控件最终完成,所以可以绕开属性设定,通过属性的编辑器实现枚举值的信息本地化。示例如下:(代码及思路来源于网络,感谢先辈们)
public enum Test//测试枚举类
{
[LocalizeDescription("ENUM_A")]
aaaa = 0,
[LocalizeDescription("ENUM_B")]
bbbb,
[LocalizeDescription("ENUM_C")]
cccc
}
class ...//测试类,含Test枚举类型属性tTest
{
private Test tTest;
...
[EditorAttribute(typeof(EnumTypeEditor), typeof(System.Drawing.Design.UITypeEditor)), ]
[TypeConverter(typeof(EnumConverter))]
public Test TTest
{
get { return test; }
set { test = value; }
}
...
}
//枚举类型编辑器
public class EnumTypeEditor : UITypeEditor {
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
//下拉列表方式,即当在PropertyGrid中选中对象的属性后会显示一个下拉列表按钮
}
//控制编辑时数据的显示方式,并返回该一个值作为属性的值。
public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService editorService = null;
if (provider != null)
{
editorService =provider.GetService( typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
}
if (editorService != null)
{
//创建显示枚举类型数据的中文描述的控件
UserCombox mComBox = new UserCombox((Enum)value,editorService);
//展开下拉列表在下拉区域显示传入的控件
editorService.DropDownControl(mComBox);
//将列表控件中用户选定的值赋值给需要返回的Value
value = mComBox.MyValue;
}
return value;
}
}
//枚举类型的类型转换器 ,实现枚举值到用户可视信息的转换
class EnumConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return true;
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
List<KeyValuePair<Enum, string>> mList = UserCombox.ToListForBind(value.GetType());
foreach (KeyValuePair<Enum, string> mItem in mList)
{
if (mItem.Key.Equals(value))
{
return mItem.Value;
}
}
return "Error!";
}
}
public partial class UserCombox : UserControl
{
private IWindowsFormsEditorService mEditorService = null;
private Enum curValue;
//传入枚举值,以及windowsFormsEditorService控件
public UserCombox(Enum mValue, IWindowsFormsEditorService editorService)
{
InitializeComponent();
mEditorService = editorService;
curValue = mValue;
if (mValue != null)
{
mCurEnumType = mValue.GetType();
if (mCurEnumType != null)
{
if (mCurEnumType.IsEnum)
{
listBox1.DataSource = ToListForBind(mCurEnumType);
listBox1.ValueMember = "Key"; listBox1.DisplayMember = "Value";
//根据当前对象的枚举值设置列表中应该选中的项
listBox1.SelectedValue = curValue;
//设置列表控件的高度为列表项能够全部显示出来并且再多5个像素
this.Height = listBox1.ItemHeight * listBox1.Items.Count + 5;
}
}
}
}
private Type mCurEnumType;
private Enum mValue;
public Enum MyValue
{
get { mValue = this.listBox1.SelectedValue as Enum; return mValue; }
}
//读取枚举类型的描述信息生成显示数据用的列表数据。
public static List<KeyValuePair<Enum, string>> ToListForBind(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (!type.IsEnum)
{
throw new ArgumentException("类型必须是枚举型的!", "type");
}
List<KeyValuePair<Enum, string>> list = new List<KeyValuePair<Enum, string>>();
list.Clear();
Array enumValues = Enum.GetValues(type);
foreach (Enum value in enumValues)
{
list.Add(new KeyValuePair<Enum, string>(value, GetDescription(value)));
}
return list;
}
#region GetDescription
/// <summary>
/// 获取枚举类型值的描述信息.
/// </summary>
/// <param name="value">枚举类型<see cref="Enum"/>值</param>
/// <returns>A string containing the text of the <see cref="DescriptionAttribute"/>.</returns>
public static string GetDescription(Enum value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
//EnumDescriptionAttribute[] attributes = (EnumDescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
else
{
description = value.ToString();
}
return description;
}
#endregion
//用户选择一项后就收起列表控件
void ListBox1SelectedIndexChanged(object sender, EventArgs e)
{
mEditorService.CloseDropDown();
}
}