七、 编写和显示订制的类型
属性窗口中的编辑有如下三种工作方式:一,有些场合可以作为字符串来编辑,然后由 TypeConverter 来实现类型的转换。二,可以显示一个下拉列表来选择值。三,一个省略按钮提供其他的 UI 界面来编辑值,比如 FileDialog 和 FontPicker 。我们已经讲过了字符串形式,接着我们就来看下拉列表。
.NET framework 已经包含了好几种下拉列表的例子,如 Color , AccessibleRole , Dock 等属性。我们从下图可以看到下拉列表的具体实现。
** 图 4. 下拉列表编辑器 **
实现下拉的工作同样是由 TypeConverter 来定义。如果看 TypeConverter 的说明,可以看到有三个虚函数来实现这个功能: GetStandardValuesSupported() , GetStandardValues() 和 GetStandardValuesExclusive() 。重载这些方法,我们可以为属性提供预先定义好的值列表。实际上,是 TypeConverter 实现了下拉列表中的枚举值。属性窗口自己本身并没有代码来处理这种下拉列表的编辑,而仅仅是使用 TypeConverter 的方法。
举个例来说,我们有一个包含 Relation 属性的 FamilyMember 组件,允许用户选择与其他人之间的关系。如果要使设计时界面更友好的话,属性窗口应该使用下拉列表来提供一些常用值的选择:如 mother , father , daughter 和 sister 等。除了提供的常用值之外,组件使用者也可以输入其他的表示关系的字符串值。
public class FamilyMember : Component
{
private string relation = "Unknown";
[TypeConverter(typeof(RelationConverter)),Category("Details")]
public string Relation
{
get { return relation;}
set { this.relation = value;}
}
}
internal class RelationConverter : StringConverter
{
private static StandardValuesCollection defaultRelations =
new StandardValuesCollection(
new string[]{"Mother", "Father", "Sister",
"Brother", "Daughter", "Son",
"Aunt", "Uncle", "Cousin"});
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
// returning false here means the property will
// have a drop down and a value that can be manually
// entered.
return false;
}
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
return defaultRelations;
}
}
不过如何做一个更加定制化的 UI 呢?我们可以使用 UITypeEditor 类。 UITypeEditor 类包括了在显示属性或者是编辑属性(比如下拉列表和省略按钮)时可以由属性窗口调用的方法。
一些类似于 Image , Color , Font.Name 的属性类型会在属性值的左边有一个小的图形化表示,这是通过重载 UITypeEditor 的 PaintValue 方法实现的。当属性窗口得到定义了编辑器的属性值的时候,它就提供给编辑器一个矩形框对象( Rectangle )和画图的对象( Graphic ),他们都包含在 PaintValue 方法的事件参数 PaintValueEventArgs 中。举个例子来说,我们有一个 Grade 类需要有图形化的表示。下面就是我们的 Grade 类。
[Editor(typeof(GradeEditor), typeof(System.Drawing.Design.UITypeEditor))]
[TypeConverter(typeof(GradeConverter))]
public struct Grade
{
private int grade;
public Grade(int grade)
{
this.grade = grade;
}
public int Value
{
get
{
return grade;
}
}
}
当我们输入一个年龄的时候,我们可以看到左边的一个图形表示。
** 图 5. 输入年龄 **
实现它并不困难。注意到赋给 Grade 类的 EditorAttribute 特性,它就是下面的这个类:
public class GradeEditor : UITypeEditor
{
public override bool GetPaintValueSupported(
ITypeDescriptorContext context)
{
// let the property browser know we'd like
// to do custom painting.
return true;
}
public override void PaintValue(PaintValueEventArgs pe)
{
// choose the right bitmap based on the value
string bmpName = null;
Grade g = (Grade)pe.Value;
if (g.Value > 80)
{
bmpName = "best.bmp";
}
else if (g.Value > 60)
{
bmpName = "ok.bmp";
}
else
{
bmpName = "bad.bmp";
}
// draw that bitmap onto the surface provided.
Bitmap b = new Bitmap(typeof(GradeEditor), bmpName);
pe.Graphics.DrawImage(b, pe.Bounds);
b.Dispose();
}
}
像我们上面提到的, UITypeEditor 可以实现属性的下拉选择和弹出对话框选择。后面的例子会包括这样的代码。如果想知道进一步的信息的话,就要参考 UITypeEditor.GetEditStyle 和 UITypeEditor.EditValue 方法以及 IWindowsFormsEditorService 接口。
-----------------------------------
<<<<<<<<<<<<待续>>>>>>>>>>>>