1.如何让某段代码只在运行阶段运行,不在设计阶段运行?
2.有哪些与属性浏览器(Property Browser)有关的功能?
3.如何设置属性的默认值?
4.怎样控制生成初始化代码?
5.什么是外来属性?怎么样实现外来属性?
详见《Windows Forms Programming in C#.NET》,本文只是作归纳。
书中相关源代码下载:ClockControl.rar (这是一个时钟控件)
1.如何让某段代码只在运行阶段运行,不在设计阶段运行?
答:使用DesignMode属性。例如ClockControl中OnPaint事件有一段代码:
Code
1 DateTime now;
2 if( this.DesignMode ) {
3 // Get pretty date/time for design-time
4 now = new DateTime(2002, 12, 31, 15, 00, 20, 0);
5 }
6 else {
7 // Get current date/time and apply the time zone modifier
8 now = DateTime.Now.AddHours(timeZoneModifier);
9 }
这样就保证在设计阶段的时间永远显示2002年12月31日,而在运行阶段则会根据实际情况设置。
2.有哪些与属性浏览器(Property Browser)有关的功能?
答:System.ComponentModel名字空间提供了一些有用的特性来帮助程序员修改他们的控件在属性浏览器中的行为和外观:
1>AmbientValueAttribute
被修饰属性将从其他来源(通常是其容器)获得属性值。
2>BrowsableAttribute
被修饰属性是否在属性浏览器中可见。例如ClockControl有一个属性IsItTimeForABreak不希望出现在属性浏览器:
Code
1 [
2 Browsable(false),
3 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
4 ]
5 public bool IsItTimeForABreak {
6 get { return isItTimeForABreak; }
7 set { isItTimeForABreak = value; }
8 }
3>CategoryAttribute
被修饰属性出现在属性浏览器中的哪一个属性组。
DescriptionAttribute
被修饰属性在属性浏览器的描述条中的文本。例如ClockControl的属性PrimaryAlarm和BackupAlarm将出现在"行为"属性组并有一段描述:
Code
1 [
2 Category("Behavior"),
3 Description("Primary alarmfor the light-sleepers.")
4 ]
5 public DateTime PrimaryAlarm {
6 get { return primaryAlarm; }
7 set {
8 if( !initializing ) {
9 if( value >= backupAlarm )
10 throw new ArgumentOutOfRangeException("Primary alarm must be before Backup alarm");
11 }
12 primaryAlarm = value;
13 }
14 }
15
16 [
17 Category("Behavior"),
18 Description("Backup alarmfor the late-nighters.")
19 ]
20 public DateTime BackupAlarm {
21 get { return backupAlarm; }
22 set {
23 if( !initializing ) {
24 if( value < primaryAlarm )
25 throw new ArgumentOutOfRangeException("Backup alarm must be after Primary alarm");
26 }
27 backupAlarm = value;
28 }
29 }
30
4>DesignOnlyAttribute
被修饰属性的值将被写入其容器窗体的资源文件里。这个特性通常用于修饰不需要存在于运行阶段的属性。
5>MergablePropertyAttribute
被修饰属性可以在程序员选中并编辑一个以上的属性时与来自其他对象的属性合并在一起。
6>ParentsizePropertyAttribute
被修饰属性在属性浏览器中是否可以被括在括号。
7>ReadOnlyAttribute
不允许被修饰属性在属性浏览器中被编辑。
3.如何设置属性的默认值?
答:使用DefaultValueAttribute特性来修饰。例如ClockControl的Face属性,它用于决定控件显示时间的样式(指针,数字还是指针和数字),它默认的值为ClockFace.Both-既显示指针,又显示数字:
Code
1 [
2 Category("Appearance"),
3 Description("Determines which style of clock face to display"),
4 DefaultValue(ClockFace.Both),
5 Editor(typeof(FaceEditor), typeof(System.Drawing.Design.UITypeEditor))
6 ]
7 public ClockFace Face {
8 get { return face; }
9 set {
10 face = value;
11 this.Invalidate();
12 }
13 }
14
如何设置类(控件)的默认事件?
答:使用DefaultEventAttribute特性来修饰。例如:
Code
1 [
2 ToolboxBitmap(typeof(ClockControlLibrary.ClockControl), "images.ClockControl.ico"),
3 DefaultEvent("AlarmSounded"),
4 DefaultProperty("Face"),
5 ProvideProperty("TimeZoneModifier", typeof(Control)),
6 Designer(typeof(ClockControlDesigner))
7 ]
8 public class ClockControl : System.Windows.Forms.Control, ISupportInitialize, IExtenderProvider {
9 //
10 }
11
将AlarmSounded事件定义为ClockControl控件的默认事件,当我们双击ClockControl控件时,Designer将自动把它与默认事件AlarmSounded关联起来;Designer将在InitializeComponent方法中生成如下代码来注册给定事件:
Code
this.clockControl1.AlarmSounded += new ClockControlLibrary.ClockControl.AlarmHandler(this.clockControl1_AlarmSounded);
并提供一个相应的事件处理器:
Code
private void clockControl1_AlarmSounded(object sender, ClockControlLibrary.AlarmEventArgs e) {
//
}
如何设置类(控件)的默认属性?
答:使用DefaultEventAttribute特性来修饰。该特性将是Designer在你第一次编辑该控件给定属性时吧光标放在他的默认属性上。
4.怎样控制生成初始化代码?
答:使用DesignerSerializationVisibilityAttribute特性,参数是DesignerSerializationVisibility枚举的值。
DesignerSerializationVisibility枚举的定义:
Code
1 // 指定属性对设计时序列化程序所具有的可见性。
2 [ComVisible(true)]
3 public enum DesignerSerializationVisibility
4 {
5 // 代码生成器不生成对象的代码。
6 Hidden = 0,
7 // 代码生成器生成对象的代码。
8 Visible = 1,
9 // 代码生成器产生对象内容的代码,而不是对象本身的代码。
10 Content = 2,
11 }
12
默认参数为Visible,其含义是:只要当某个属性的当前值与它默认值(如果有默认值的话)不一样,Designer就在InitializeComponent方法中生成该属性进行初始化的代码。如果不想让Designer生成对某个属性进行初始化的代码,则应该使用Hidden作为参数(可以把Hidden和BrowsableAttribute特性(设为False)一起使用来修饰只在运行阶段才有意义的属性!)。
为了让Designer为包含定制类型的属性生成初始化代码,应该使用Content作为参数。例如ClockControl的MessageToSelf属性用来实现"日程提示"功能:
Code
1 [
2 Category("Behavior"),
3 Description("Stuff to remember for later"),
4 DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
5 ]
6 public MessageToSelfCollection MessagesToSelf {
7 get {
8 if( messagesToSelf == null ) messagesToSelf = new MessageToSelfCollection();
9 return messagesToSelf;
10 }
11 set { messagesToSelf = value; }
12 }
13
下面是Designer为单条消息生成的初始化代码:
Code
1this.clockControl1.MessagesToSelf.AddRange(new ClockControlLibrary.MessageToSelf[] {new ClockControlLibrary.MessageToSelf(new System.DateTime(2003, 2, 22, 21, 55, 0, 0), "Wake up")});
2
这段代码还需要一个类型转换器。
5.什么是外来属性?怎么样实现外来属性?
答:简单的说外来属性是一个来自其他控件的属性,提供外来属性的控件被称为"提供者控件",接受外来属性的控件被称为"接受者控件",最典型的就是ToolTip组件,我们可以为给定窗体上的每一个控件的ToolTip属性进行设置。
具体到ClockControl,把TimeZoneModifier属性实现为一个让窗体上每一个控件都有其自身值的外来属性,实现步骤:
1>先用ProvidePropertyAttribute特性把属性申明为外来属性。
2>实现IExtenderProvider接口中的CanExtend方法。
3>为每一个接受外来属性的控件分别保存一个外来属性值的能力,由Get<PropertyName>和Set<PropertyName>(这里的PropertyName就是你用ProvidePropertyAttribute特性定义的属性名,具体到ClockControl,就是GetTimeZoneModifier和SetTimeZoneModifier)方法提供。
[涉及代码请自行查阅源代码!]
近期会发布相关内容:类型转换器和UI类型编辑器的设计