
2.3 Properties窗口
在Visual Studio 2015中,一个最常用的工具窗口是Properties窗口(快捷键为F4键),如图2-21所示。Properties窗口由一个属性网格组成并与上下文相关,仅显示与当前选中项相关的属性,该项可以是Solution Explorer中的一个节点,或是窗体设计区域中的一个元素。每一行都表示一个属性,且在两列中显示了属性名和对应的值。图2-21(c)显示了WPF应用程序的已更新属性网格,其中包括一个预览图标和搜索功能。

图2-21
Properties窗口可以对属性分组或按字母排序属性——使用Properties窗口顶部的前两个按钮可以切换这个布局。还有一组内置的编辑器,用于各种系统类型,如颜色、字体、锚定和停靠,在单击要修改的属性值列时,就会调用这些编辑器。选择一个属性后,如图2-21(b)所示,该属性名会突出显示,并在属性网格的下方区域显示描述。
在Properties窗口中,只读属性用灰色表示,其值不能修改。图2-21(b)中的Text属性值SayHello!显示为黑体,表示它不是这个属性的默认值。同样,图2-21(c)中的Text属性在属性名和属性值之间有一个实心黑方块,表示指定了该属性值。如果查看设计器生成的如下代码,应注意到属性网格中每个显示为黑体的属性都有一行代码——给控件中的每个属性添加一行代码会显著增加显示窗体的时间。
VB
Me.btnSayHello.Location = New System.Drawing.Point(12, 12) Me.btnSayHello.Name = "btnSayHello" Me.btnSayHello.Size = New System.Drawing.Size(100, 23) Me.btnSayHello.TabIndex = 0 Me.btnSayHello.Text = "Say Hello!" Me.btnSayHello.UseVisualStyleBackColor = True
C#
this.btnSayHello.Location = new System.Drawing.Point(12, 12); this.btnSayHello.Name = "btnSayHello"; this.btnSayHello.Size = new System.Drawing.Size(100, 23); this.btnSayHello.TabIndex = 0; this.btnSayHello.Text = "Say Hello!"; this.btnSayHello.UseVisualStyleBackColor = true;
对于Web和WPF应用程序,Properties窗口中的属性集分别保存为.aspx或.xaml文件中的标记。与Windows窗体设计器一样,只有在Properties窗口中设置的值才保存到标记中。
除了显示所选项的属性外,Properties窗口还为关联事件处理程序提供了一种设计方式。图2-22(a)显示了通过Properties窗口顶部的第4个按钮(闪电图标)打开的事件视图。这里包含了单击事件的处理程序。要关联另一个事件,可以在值列的下拉列表中从已有的方法列表中选择,也可以双击值列,这会创建一个新的事件处理方法,并把它关联到事件上。如果使用第一种方法,就只列出匹配事件签名的方法。

图2-22
某些组件,如DataGridView,有许多命令或快捷键可以通过Properties窗口来执行。图2-22(b)中的DataGridView有两个命令:Edit Columns和Add Column。点击这两个命令链接,就会显示一个执行该操作的对话框。如果命令没有立即显示,则右击Properties窗口并从上下文菜单中选择Commands命令。
如果Properties窗口仅占用屏幕的一小部分空间,就很难滚动属性列表。如果右击属性网格,就可以取消对Commands和Description复选框的选择,以隐藏Properties窗口的这些部分。
扩展Properties窗口
Visual Studio 2015使属性值显示为黑体来突出已改变的属性。问题是Visual Studio 2015如何知道默认值是什么?答案是Properties窗口查询一个对象以确定在属性网格中显示什么属性时,它会查找许多设计特性。这些特性可以用来控制要显示的属性、编辑其值的编辑器以及属性的默认值。为了说明如何在自己的组件中使用这些特性,先给组件添加一个简单的自动属性。
VB
Public Property Description As String
C#
public string Description { get; set; }
1.Browsable特性
所有的公共属性都默认显示在属性网格中。但通过添加Browsable特性,可以明确控制这个行为。如果把该特性设置为false,就不会在属性网格中显示属性。
VB
<System.ComponentModel.Browsable(False)> Public Property Description As String
C#
[System.ComponentModel.Browsable(false)] public string Description { get; set; }
2.DisplayName特性
DisplayName特性很容易理解,它允许修改属性的显示名称。在下面的例子中,可以修改Description属性的名称,使该属性在属性网格中显示为VS2015 Description。
VB
<System.ComponentModel.DisplayName("VS2015 Description")> Public Property Description As String
C#
[System.ComponentModel.DisplayName("VS2015 Description")] public string Description { get; set; }
3.Description特性
除了定义属性的友好名称或显示名称外,还应提供描述。在选择属性时,该描述显示在Properties窗口的底部区域,确保组件的用户了解属性的用途。
VB
<System.ComponentModel.Description("My first custom property")> Public Property Description As String
C#
[System.ComponentModel.Description("My first custom property")] public string Description { get; set; }
4.Category特性
Properties窗口处于分组视图中时,用户提供的所有属性都默认放在Misc组中。使用Category特性可以把这些属性放在任何一个已有的组中,例如Appearance或Data;如果指定了一个不存在的组名,还会把属性放在一个新组中。
VB
<System.ComponentModel.Category("Appearance")> Public Property Description As String
C#
[System.ComponentModel.Category("Appearance")] public string Description { get; set; }
5.DefaultValue特性
前面提到,Visual Studio 2015突出显示修改了初始值或默认值的属性。实际上,Visual Studio 2015会查找DefaultValue特性来确定属性的默认值。
VB
Private Const cDefaultDescription As String = "<enter description>" <System.ComponentModel.DefaultValue(cDefaultDescription)> Public Property Description As String = cDefaultDescription
C#
private const string cDefaultDescription = "<enter description>"; private string mDescription = cDefaultDescription; [System.ComponentModel.DefaultValue(cDefaultDescription)] public string Description { get { return mDescription; } set { mDescription = value; } }
在这个例子中,如果Description属性的值设置为< enter description >,Visual Studio 2015就删除设置该属性的代码行。如果修改了一个属性并希望返回其默认值,就可以在Properties窗口中右击该属性,并从上下文菜单中选择Reset命令。
注意,DefaultValue特性不会设置属性的初始值。如果指定了DefaultValue特性,建议也把属性的初始值设置为相同的值,如上面的代码所示。
6.AmbientValue特性
我们自认为已经理解但实际并未真正理解的一个功能是周围属性的概念。典型的例子是背景色、前景色和字体:除非通过Properties窗口明确设置,否则这些属性不是继承自它们的基类,而是继承自它们的父控件。周围属性的较宽泛的定义是从另一个数据源中获得其值的属性。
与DefaultValue特性一样,AmbientValue特性用于告诉Visual Studio 2015何时应不给设计器文件添加代码。但是使用周围属性,就不能硬编码设计器的值来比较当前值,因为它要视属性的源值而定。因此,在定义AmbientValue特性时,这会告诉设计器查找函数ShouldSerializePropertyName。在下面的例子中,这个函数是ShouldSerializeDescription,调用它可以确定属性的当前值是否应保存到设计器代码文件中。
VB
Private mDescription As String = cDefaultDescription <System.ComponentModel.AmbientValue(cDefaultDescription)> Public Property Description As String Get If Me.mDescription = cDefaultDescription AndAlso Me.Parent IsNot Nothing Then Return Parent.Text End If Return mDescription End Get Set(ByVal value As String) mDescription = value End Set End Property Private Function ShouldSerializeDescription()As Boolean If Me.Parent IsNot Nothing Then Return Not Me.Description = Me.Parent.Text Else Return Not Me.Description = cDefaultDescription End If End function
C#
private string mDescription = cDefaultDescription; [System.ComponentModel.AmbientValue(cDefaultDescription)] public string Description{ get{ if (this.mDescription == cDefaultDescription && this.Parent != null){ return Parent.Text; } return mDescription; } set{ mDescription = value; } } private bool ShouldSerializeDescription(){ if (this.Parent != null){ return this.Description != this.Parent.Text; } else{ return this.Description != cDefaultDescription; } }
在创建带这个属性的控件时,其初始值设置为DefaultDescription常量的值,但在设计器中,有一个值对应于Parent.Text值。在设计器代码文件中,没有代码明确设置这个属性,因为在Properties窗口中,该值没有显示为黑体。如果把这个属性的值改为不是DefaultDescription常量,该值就会显示为黑体,并在设计器代码文件中添加一行代码。如果重置这个属性,底层的值就被设置回AmbientValue定义的值,但我们只会看到它恢复为显示Parent.Text值。