充分利用 .NET 框架的 PropertyGrid 控件
Mark Rideout
Microsoft Corporation
摘要: 本文旨在帮助您了解 Microsoft .NET 框架中的 PropertyGrid 控件,以及如何针对您的应用程序自定义该控件。
适用于:
Microsoft® .NET® 框架
Microsoft® Visual Studio® .NET
目录
PropertyGrid 控件简介 创建 PropertyGrid 控件
何处使用 PropertyGrid 控件
选择对象
自定义 PropertyGrid 控件
显示复杂属性
为属性提供自定义 UI
小结
PropertyGrid 控件简介
如果您使用过 Microsoft® Visual Basic® 或 Microsoft Visual Studio .NET,那么您一定使用过属性浏览器来浏览、查看和编辑一个或多个对象的属性。.NET 框架 PropertyGrid 控件是 Visual Studio .NET 属性浏览器的核心。 PropertyGrid 控件显示对象或类型的属性,并主要通过使用反射来检索项目的属性。(反射是在运行时提供类型信息的技术。)
下面的屏幕快照显示了 PropertyGrid 在窗体上的外观。

图 1:窗体上的 PropertyGrid
PropertyGrid 包含以下部分:
- 属性
- 可展开属性
- 属性类别标题
- 属性说明
- 属性编辑器
- 属性选项卡
- 命令窗格(显示控件设计器提供的设计器操作)
创建 PropertyGrid 控件
要使用 Visual Studio .NET 创建 PropertyGrid 控件,需要将该控件添加到工具箱中,因为默认情况下并不包含该控件。在 Tools (工具)菜单中,选择 Customize Toolbox (自定义工具箱)。在对话框中选择 Framework Components (框架组件)选项卡,然后选择 PropertyGrid 。
如果您从命令行编译代码,请使用 /reference 选项并指定 System.Windows.Forms.dll。
以下代码显示了如何创建 PropertyGrid 控件并将其添加到窗体中。
**' Visual Basic** Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Globalization
Public Class OptionsDialog
Inherits System.Windows.Forms.Form
Private OptionsPropertyGrid As System.Windows.Forms.PropertyGrid
Public Sub New()
MyBase.New()
OptionsPropertyGrid = New PropertyGrid()
OptionsPropertyGrid.Size = New Size(300, 250)
Me.Controls.Add(OptionsPropertyGrid)
Me.Text = "选项对话框"
End Sub
End Class
**//C#**
using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using System.Globalization;
public class OptionsDialog : System.Windows.Forms.Form
{
private System.Windows.Forms.PropertyGrid OptionsPropertyGrid;
public OptionsDialog()
{
OptionsPropertyGrid = new PropertyGrid();
OptionsPropertyGrid.Size = new Size(300, 250);
this.Controls.Add(OptionsPropertyGrid);
this.Text = "选项对话框";
}
[STAThread]
static void Main()
{
Application.Run(new OptionsDialog());
}
}
何处使用 PropertyGrid 控件
在应用程序中的很多地方,您都可以使用户与 PropertyGrid 进行交互,从而获得更丰富的编辑体验。例如,某个应用程序包含多个用户可以设置的“设置”或选项,其中一些可能十分复杂。您可以使用单选按钮、组合框或文本框来表示这些选项。但本文将逐步介绍如何使用 PropertyGrid 控件创建选项窗口来设置应用程序选项。上面所创建的 OptionsDialog
窗体即是选项窗口的开始。现在,我们创建一个名为 AppSettings
的类,其中包含映射到应用程序设置的所有属性。如果创建单独的类而不使用多个分散的变量,设置将更便于管理和维护。
**' Visual Basic** Public Class AppSettings
Private _saveOnClose As Boolean = True
Private _greetingText As String = "欢迎使用应用程序!"
Private _maxRepeatRate As Integer = 10
Private _itemsInMRU As Integer = 4
Private _settingsChanged As Boolean = False
Private _appVersion As String = "1.0"
Public Property SaveOnClose() As Boolean
Get
Return _saveOnClose
End Get
Set(ByVal Value As Boolean)
SaveOnClose = Value
End Set
End Property
Public Property GreetingText() As String
Get
Return _greetingText
End Get
Set(ByVal Value As String)
_greetingText = Value
End Set
End Property
Public Property ItemsInMRUList() As Integer
Get
Return _itemsInMRU
End Get
Set(ByVal Value As Integer)
_itemsInMRU = Value
End Set
End Property
Public Property MaxRepeatRate() As Integer
Get
Return _maxRepeatRate
End Get
Set(ByVal Value As Integer)
_maxRepeatRate = Value
End Set
End Property
Public Property SettingsChanged() As Boolean
Get
Return _settingsChanged
End Get
Set(ByVal Value As Boolean)
_settingsChanged = Value
End Set
End Property
Public Property AppVersion() As String
Get
Return _appVersion
End Get
Set(ByVal Value As String)
_appVersion = Value
End Set
End Property
End Class
**//C#**
public class AppSettings{
private bool saveOnClose = true;
private string greetingText = "欢迎使用应用程序!";
private int itemsInMRU = 4;
private int maxRepeatRate = 10;
private bool settingsChanged = false;
private string appVersion = "1.0";
public bool SaveOnClose
{
get { return saveOnClose; }
set { saveOnClose = value;}
}
public string GreetingText
{
get { return greetingText; }
set { greetingText = value; }
}
public int MaxRepeatRate
{
get { return maxRepeatRate; }
set { maxRepeatRate = value; }
}
public int ItemsInMRUList
{
get { return itemsInMRU; }
set { itemsInMRU = value; }
}
public bool SettingsChanged
{
get { return settingsChanged; }
set { settingsChanged = value; }
}
public string AppVersion
{
get { return appVersion; }
set { appVersion = value; }
}
}
选项窗口上的 PropertyGrid 将使用此类,因此请将类定义添加到应用程序项目中,在添加时可创建新文件或将其添加到现有窗体源代码的下方。
选择对象
要标识 PropertyGrid 显示的内容,请将 PropertyGrid.SelectedObject 属性设置为一个对象实例。然后, PropertyGrid 将完成其余的工作。每次设置 SelectedObject 时, PropertyGrid 都会刷新显示的属性。这提供了一种简单的方法来强制刷新属性,或在运行时切换对象。您还可以调用 PropertyGrid.Refresh 方法来刷新属性。
接下来,您需要更新 OptionsDialog
构造函数中的代码,以创建一个 AppSettings
对象,并将其设置为 PropertyGrid.SelectedObject 属性的值。
**' Visual Basic** Public Sub New()
MyBase.New()
OptionsPropertyGrid = New PropertyGrid()
OptionsPropertyGrid.Size = New Size(300, 250)
Me.Controls.Add(OptionsPropertyGrid)
Me.Text = "选项对话框"
**' 创建 AppSettings 类并在 PropertyGrid 中显示该类。**
**Dim appset as AppSettings = New AppSettings()**
**OptionsPropertyGrid.SelectedObject = appset**
End Sub
**//C#**
public OptionsDialog()
{
OptionsPropertyGrid = new PropertyGrid();
OptionsPropertyGrid.Size = new Size(300, 250);
this.Controls.Add(OptionsPropertyGrid);
this.Text = "选项对话框";
**// 创建 AppSettings 类并在 PropertyGrid 中显示该类。**
**AppSettings appset = new AppSettings();**
**OptionsPropertyGrid.SelectedObject = appset;**
}
编译并运行该应用程序。下面的屏幕快照显示了应用程序的外观。

图 2:PropertyGrid 中选定的 AppSettings 类
自定义 PropertyGrid 控件
您可以修改 PropertyGrid 的某些外观特征以满足自己的需要。可以更改某些属性的显示方式,甚至选择不显示某些属性。那么,如何对 PropertyGrid 进行自定义呢?
更改 PropertyGrid 的外观特征
PropertyGrid 的许多外观特征都可以自定义。下面列出了其中的一部分:
- 通过 HelpBackColor 、 HelpForeColor 和 HelpVisible 属性可以更改背景颜色、更改字体颜色或隐藏说明窗格。
- 通过 ToolbarVisible 属性可以隐藏工具栏,通过 BackColor 属性可以更改工具栏的颜色,通过 LargeButtons 属性可以显示大工具栏按钮。
- 使用 PropertySort 属性可以按字母顺序对属性进行排序和分类。
- 通过 BackColor 属性可以更改拆分器的颜色。
- 通过 LineColor 属性可以更改网格线和边框。
本示例中的选项窗口不需要工具栏,因此可以将 ToolbarVisible 设置为 false 。其余属性均保留默认设置。
更改属性的显示方式
要更改某些属性的显示方式,您可以对这些属性应用不同的特性。特性是用于为类型、字段、方法和属性等编程元素添加批注的声明标记,在运行时可以使用反射对其进行检索。下面列出了其中的一部分:
- DescriptionAttribute - 设置显示在属性下方说明帮助窗格中的属性文本。这是一种为活动属性(即具有焦点的属性)提供帮助文本的有效方法。可以将此特性应用于
MaxRepeatRate
属性。 - CategoryAttribute - 设置属性在网格中所属的类别。当您需要将属性按类别名称分组时,此特性非常有用。如果没有为属性指定类别,该属性将被分配给 杂项 类别。可以将此特性应用于所有属性。
- BrowsableAttribute – 表示是否在网格中显示属性。此特性可用于在网格中隐藏属性。默认情况下,公共属性始终显示在网格中。可以将此特性应用于
SettingsChanged
属性。 - ReadOnlyAttribute – 表示属性是否为只读。此特性可用于禁止在网格中编辑属性。默认情况下,带有 get 和 set 访问函数的公共属性在网格中是可以编辑的。可以将此特性应用于
AppVersion
属性。 - DefaultValueAttribute – 表示属性的默认值。如果希望为属性提供默认值,然后确定该属性值是否与默认值相同,则可使用此特性。可以将此特性应用于所有属性。
- DefaultPropertyAttribute – 表示类的默认属性。在网格中选择某个类时,将首先突出显示该类的默认属性。可以将此特性应用于
AppSettings
类。
现在,我们将其中的一些特性应用于 AppSettings
类,以更改属性在 PropertyGrid 中的显示方式。
**' Visual Basic** **< DefaultPropertyAttribute("SaveOnClose")> _**
Public Class AppSettings
Private _saveOnClose As Boolean = True
Private _greetingText As String = "欢迎使用应用程序!"
Private _maxRepeatRate As Integer = 10
Private _itemsInMRU As Integer = 4
Private _settingsChanged As Boolean = False
Private _appVersion As String = "1.0"
**
1<categoryattribute("文档设置"), **defaultvalueattribute(true)="" _**=""> _**
2 Public Property SaveOnClose() As Boolean
3 Get
4 Return _saveOnClose
5 End Get
6 Set(ByVal Value As Boolean)
7 SaveOnClose = Value
8 End Set
9 End Property
10
11 ** <categoryattribute("全局设置"), **defaultvalueattribute("欢迎使用应用程序!")="" **readonlyattribute(true),="" _**=""> _**
12 Public Property GreetingText() As String
13 Get
14 Return _greetingText
15 End Get
16 Set(ByVal Value As String)
17 _greetingText = Value
18 End Set
19 End Property
20
21 ** <categoryattribute("全局设置"), **defaultvalueattribute(4)="" _**=""> _**
22 Public Property ItemsInMRUList() As Integer
23 Get
24 Return _itemsInMRU
25 End Get
26 Set(ByVal Value As Integer)
27 _itemsInMRU = Value
28 End Set
29 End Property
30
31 ** <descriptionattribute("以毫秒表示的文本重复率。"), **categoryattribute("全局设置"),="" **defaultvalueattribute(10)="" _**=""> _**
32 Public Property MaxRepeatRate() As Integer
33 Get
34 Return _maxRepeatRate
35 End Get
36 Set(ByVal Value As Integer)
37 _maxRepeatRate = Value
38 End Set
39 End Property
40
41 ** <browsableattribute(false),** **defaultvalueattribute(false)=""> _**
42 Public Property SettingsChanged() As Boolean
43 Get
44 Return _settingsChanged
45 End Get
46 Set(ByVal Value As Boolean)
47 _settingsChanged = Value
48 End Set
49 End Property
50
51 ** <categoryattribute("版本"), **defaultvalueattribute("1.0"),="" **readonlyattribute(true)="" _**=""> _**
52 Public Property AppVersion() As String
53 Get
54 Return _appVersion
55 End Get
56 Set(ByVal Value As String)
57 _appVersion = Value
58 End Set
59 End Property
60 End Class
61
62
63 **//C#**
64 **[DefaultPropertyAttribute("SaveOnClose")]**
65 public class AppSettings{
66 private bool saveOnClose = true;
67 private string greetingText = "欢迎使用应用程序!";
68 private int maxRepeatRate = 10;
69 private int itemsInMRU = 4;
70
71 private bool settingsChanged = false;
72 private string appVersion = "1.0";
73
74 **[CategoryAttribute("文档设置"),**
75 **DefaultValueAttribute(true)]**
76 public bool SaveOnClose
77 {
78 get { return saveOnClose; }
79 set { saveOnClose = value;}
80 }
81
82 **[CategoryAttribute("全局设置"),**
83 **ReadOnlyAttribute(true),**
84 **DefaultValueAttribute("欢迎使用应用程序!")]**
85 public string GreetingText
86 {
87 get { return greetingText; }
88 set { greetingText = value; }
89 }
90
91 **[CategoryAttribute("全局设置"),**
92 **DefaultValueAttribute(4)]**
93 public int ItemsInMRUList
94 {
95 get { return itemsInMRU; }
96 set { itemsInMRU = value; }
97 }
98
99 **[DescriptionAttribute("以毫秒表示的文本重复率。"),**
100 **CategoryAttribute("全局设置"),**
101 **DefaultValueAttribute(10)]**
102 public int MaxRepeatRate
103 {
104 get { return maxRepeatRate; }
105 set { maxRepeatRate = value; }
106 }
107
108 **[BrowsableAttribute(false),**
109 **DefaultValueAttribute(false)]**
110 public bool SettingsChanged
111 {
112 get { return settingsChanged; }
113 set { settingsChanged = value; }
114 }
115
116 **[CategoryAttribute("版本"),**
117 **DefaultValueAttribute("1.0"),**
118 **ReadOnlyAttribute(true)]**
119 public string AppVersion
120 {
121 get { return appVersion; }
122 set { appVersion = value; }
123 }
124 }
125
126将这些特性应用于 ` AppSettings ` 类后,编译并运行该应用程序。下面的屏幕快照显示了应用程序的外观。
127
128`  `
129
130**图 3:PropertyGrid 中显示的带有类别和默认值的属性**
131
132使用此版本的选项窗口后,您会注意到以下几点:
133
134 * 显示窗口时,将首先突出显示 ` SaveOnClose ` 属性。
135 * 选中 ` MaxRepeatRate ` 属性时,说明帮助窗格中将显示“以毫秒表示的文本重复率”。
136 * ` SaveOnClose ` 属性显示在“文档设置”类别下。其他属性分别显示在“全局设置”和“版本”类别下。
137 * ` SettingsChanged ` 属性将不再显示。
138 * ` AppVersion ` 属性为只读。只读属性以灰显文本显示。
139 * 如果 ` SaveOnClose ` 属性包含的值不是 **true** ,该值将以粗体显示。 **PropertyGrid** 使用粗体文本表示包含非默认值的属性。
140
141
142
143## 显示复杂属性
144
145到现在为止,选项窗口显示的都是简单的类型,如整数、布尔值和字符串。那么,如何显示更复杂的类型呢?如果应用程序需要跟踪窗口大小、文档字体或工具栏颜色等信息,该如何处理呢?.NET 框架提供的某些数据类型具有特殊的显示功能,能使这些类型在 **PropertyGrid** 中更具可用性。
146
147### 对所提供类型的支持
148
149首先,请更新 ` AppSettings ` 类,为窗口大小( **Size** 类型)、窗口字体( **Font** 类型)和工具栏颜色( **Color** 类型)添加新属性。
150
151
152 **' Visual Basic** <defaultpropertyattribute("saveonclose")> _
153 Public Class AppSettings
154 Private _saveOnClose As Boolean = True
155 Private _greetingText As String = "欢迎使用应用程序!"
156 Private _maxRepeatRate As Integer = 10
157 Private _itemsInMRU As Integer = 4
158
159 Private _settingsChanged As Boolean = False
160 Private _appVersion As String = "1.0"
161
162 **Private _windowSize As Size = New Size(100, 100)**
163 **Private _windowFont As Font = New Font("宋体", 9, FontStyle.Regular)**
164 **Private _toolbarColor As Color = SystemColors.Control**
165
166 <categoryattribute("文档设置"), _="" defaultvalueattribute(true)=""> _
167 Public Property SaveOnClose() As Boolean
168 Get
169 Return _saveOnClose
170 End Get
171 Set(ByVal Value As Boolean)
172 SaveOnClose = Value
173 End Set
174 End Property
175
176 ** <categoryattribute("文档设置")> _**
177 **Public Property WindowSize() As Size**
178 **Get**
179 **Return _windowSize**
180 **End Get**
181 **Set(ByVal Value As Size)**
182 **_windowSize = Value**
183 **End Set**
184 **End Property**
185
186 ** <categoryattribute("文档设置")> _**
187 **Public Property WindowFont() As Font**
188 **Get**
189 **Return _windowFont**
190 **End Get**
191 **Set(ByVal Value As Font)**
192 **_windowFont = Value**
193 **End Set**
194 **End Property**
195
196 ** <categoryattribute("全局设置")> _**
197 **Public Property ToolbarColor() As Color**
198 **Get**
199 **Return _toolbarColor**
200 **End Get**
201 **Set(ByVal Value As Color)**
202 **_toolbarColor = Value**
203 **End Set**
204 **End Property**
205
206 <categoryattribute("全局设置"), _="" defaultvalueattribute("欢迎使用应用程序!")="" readonlyattribute(true),=""> _
207 Public Property GreetingText() As String
208 Get
209 Return _greetingText
210 End Get
211 Set(ByVal Value As String)
212 _greetingText = Value
213 End Set
214 End Property
215
216 <categoryattribute("全局设置"), _="" defaultvalueattribute(4)=""> _
217 Public Property ItemsInMRUList() As Integer
218 Get
219 Return _itemsInMRU
220 End Get
221 Set(ByVal Value As Integer)
222 _itemsInMRU = Value
223 End Set
224 End Property
225
226 <descriptionattribute("以毫秒表示的文本重复率。"), _="" categoryattribute("全局设置"),="" defaultvalueattribute(10)=""> _
227 Public Property MaxRepeatRate() As Integer
228 Get
229 Return _maxRepeatRate
230 End Get
231 Set(ByVal Value As Integer)
232 _maxRepeatRate = Value
233 End Set
234 End Property
235
236 <browsableattribute(false), defaultvalueattribute(false)=""> _
237 Public Property SettingsChanged() As Boolean
238 Get
239 Return _settingsChanged
240 End Get
241 Set(ByVal Value As Boolean)
242 _settingsChanged = Value
243 End Set
244 End Property
245
246 <categoryattribute("版本"), _="" defaultvalueattribute("1.0"),="" readonlyattribute(true)=""> _
247 Public Property AppVersion() As String
248 Get
249 Return _appVersion
250 End Get
251 Set(ByVal Value As String)
252 _appVersion = Value
253 End Set
254 End Property
255 End Class
256
257
258 **//C#**
259
260 [DefaultPropertyAttribute("SaveOnClose")]
261 public class AppSettings{
262 private bool saveOnClose = true;
263 private string greetingText = "欢迎使用应用程序!";
264 private int maxRepeatRate = 10;
265 private int itemsInMRU = 4;
266
267 private bool settingsChanged = false;
268 private string appVersion = "1.0";
269
270 **private Size windowSize = new Size(100,100);**
271 **private Font windowFont = new Font("宋体", 9, FontStyle.Regular);**
272 **private Color toolbarColor = SystemColors.Control;**
273
274 [CategoryAttribute("文档设置"),
275 DefaultValueAttribute(true)]
276 public bool SaveOnClose
277 {
278 get { return saveOnClose; }
279 set { saveOnClose = value;}
280 }
281
282 **[CategoryAttribute("文档设置")]**
283 **public Size WindowSize**
284 **{**
285 **get { return windowSize; }**
286 **set { windowSize = value;}**
287 **}**
288
289 **[CategoryAttribute("文档设置")]**
290 **public Font WindowFont**
291 **{**
292 **get {return windowFont; }**
293 **set { windowFont = value;}**
294 **}**
295
296 **[CategoryAttribute("全局设置")]**
297 **public Color ToolbarColor**
298 **{**
299 **get { return toolbarColor; }**
300 **set { toolbarColor = value; }**
301 **}**
302
303 [CategoryAttribute("全局设置"),
304 ReadOnlyAttribute(true),
305 DefaultValueAttribute("欢迎使用应用程序!")]
306 public string GreetingText
307 {
308 get { return greetingText; }
309 set { greetingText = value; }
310 }
311
312 [CategoryAttribute("全局设置"),
313 DefaultValueAttribute(4)]
314 public int ItemsInMRUList
315 {
316 get { return itemsInMRU; }
317 set { itemsInMRU = value; }
318 }
319
320 [DescriptionAttribute("以毫秒表示的文本重复率。"),
321 CategoryAttribute("全局设置"),
322 DefaultValueAttribute(10)]
323 public int MaxRepeatRate
324 {
325 get { return maxRepeatRate; }
326 set { maxRepeatRate = value; }
327 }
328
329 [BrowsableAttribute(false),
330 DefaultValueAttribute(false)]
331 public bool SettingsChanged
332 {
333 get { return settingsChanged; }
334 set { settingsChanged = value; }
335 }
336
337 [CategoryAttribute("版本"),
338 DefaultValueAttribute("1.0"),
339 ReadOnlyAttribute(true)]
340 public string AppVersion
341 {
342 get { return appVersion; }
343 set { appVersion = value; }
344 }
345 }
346
347下面的屏幕快照显示了新属性在 **PropertyGrid** 中的外观。
348
349`  `
350
351**图 4:显示在 PropertyGrid 中的 .NET 框架数据类型**
352
353请注意, ` WindowFont ` 属性带有一个省略号 (...) 按钮,按下该按钮将显示字体选择对话框。此外,还可以展开该属性以显示更多的 **Font** 属性。某些 **Font** 属性提供有关字体的值和详细信息的下拉列表。您可以展开 ` WindowSize ` 属性以显示 **Size** 类型的更多属性。最后,请注意, ` ToolbarColor ` 属性包含一个选定颜色的样本,以及一个用于选择不同颜色的自定义下拉列表。对于这些以及其他数据类型,.NET 框架提供了其他的类,可以使在 **PropertyGrid** 中的编辑更加容易。
354
355### 对自定义类型的支持
356
357现在,您需要在 ` AppSettings ` 类中添加另外两个属性,即 ` DefaultFileName ` 和 ` SpellCheckOptions ` 。 ` DefaultFileName ` 属性用于获取或设置字符串; ` SpellCheckOptions ` 属性用于获取或设置 ` SpellingOptions ` 类的实例。
358
359` SpellingOptions ` 类是一个新类,用于管理应用程序的拼写检查属性。对于何时创建单独的类以管理对象的属性,并没有严格的规定,而取决于您的整个类设计。将 ` SpellingOptions ` 类定义添加到应用程序项目中 - 可以添加到新文件中,也可以添加到窗体源代码的下方。
360
361
362 **' Visual Basic** <descriptionattribute("展开以查看应用程序的拼写选项。")> _
363 Public Class SpellingOptions
364 Private _spellCheckWhileTyping As Boolean = True
365 Private _spellCheckCAPS As Boolean = False
366 Private _suggestCorrections As Boolean = True
367
368 <defaultvalueattribute(true)> _
369 Public Property SpellCheckWhileTyping() As Boolean
370 Get
371 Return _spellCheckWhileTyping
372 End Get
373 Set(ByVal Value As Boolean)
374 _spellCheckWhileTyping = Value
375 End Set
376 End Property
377
378 <defaultvalueattribute(false)> _
379 Public Property SpellCheckCAPS() As Boolean
380 Get
381 Return _spellCheckCAPS
382 End Get
383 Set(ByVal Value As Boolean)
384 _spellCheckCAPS = Value
385 End Set
386 End Property
387
388 <defaultvalueattribute(true)> _
389 Public Property SuggestCorrections() As Boolean
390 Get
391 Return _suggestCorrections
392 End Get
393 Set(ByVal Value As Boolean)
394 _suggestCorrections = Value
395 End Set
396 End Property
397 End Class
398
399
400 **//C#**
401
402 [DescriptionAttribute("展开以查看应用程序的拼写选项。")]
403 public class SpellingOptions{
404 private bool spellCheckWhileTyping = true;
405 private bool spellCheckCAPS = false;
406 private bool suggestCorrections = true;
407
408 [DefaultValueAttribute(true)]
409 public bool SpellCheckWhileTyping
410 {
411 get { return spellCheckWhileTyping; }
412 set { spellCheckWhileTyping = value; }
413 }
414
415 [DefaultValueAttribute(false)]
416 public bool SpellCheckCAPS
417 {
418 get { return spellCheckCAPS; }
419 set { spellCheckCAPS = value; }
420 }
421 [DefaultValueAttribute(true)]
422 public bool SuggestCorrections
423 {
424 get { return suggestCorrections; }
425 set { suggestCorrections = value; }
426 }
427 }
428
429再次编译并运行选项窗口应用程序。下面的屏幕快照显示了应用程序的外观。
430
431`  `
432
433**图 5:在 PropertyGrid 中显示的不带类型转换器的自定义数据类型**
434
435请注意 ` SpellcheckOptions ` 属性的外观。与 .NET 框架类型不同,它不展开或显示自定义的字符串表示。如果要在自己的复杂类型中提供与 .NET 框架类型相同的编辑体验,该如何处理呢?.NET 框架类型使用 **TypeConverter** 和 **UITypeEditor** 类提供大部分 **PropertyGrid** 编辑支持,您也可以使用这些类。
436
437#### 添加可展开属性支持
438
439要使 **PropertyGrid** 能够展开 ` SpellingOptions ` 属性,您需要创建 **TypeConverter** 。 **TypeConverter** 提供了从一种类型转换为另一种类型的方法。 **PropertyGrid** 使用 **TypeConverter** 将对象类型转换为 **String** ,并使用该 **String** 在网格中显示对象值。在编辑过程中, **TypeConverter** 会将 **String** 转换回对象类型。.NET 框架提供的 **ExpandableObjectConverter** 类可以简化这一过程。
440
441#### 提供可展开对象支持
442
443 1. 创建一个从 **ExpandableObjectConverter** 继承而来的类。
444
445 **' Visual Basic** Public Class SpellingOptionsConverter
446 Inherits ExpandableObjectConverter
447 End Class
448
449
450 **//C#**
451
452 public class SpellingOptionsConverter:ExpandableObjectConverter
453 { }
454
455 2. 如果 ` destinationType ` 参数与使用此类型转换器的类(示例中的 ` SpellingOptions ` 类)的类型相同,则覆盖 **CanConvertTo** 方法并返回 **true** ;否则返回基类 **CanConvertTo** 方法的值。
456
457 **' Visual Basic** Public Overloads Overrides Function CanConvertTo( _
458 ByVal context As ITypeDescriptorContext, _
459 ByVal destinationType As Type) As Boolean
460 If (destinationType Is GetType(SpellingOptions)) Then
461 Return True
462 End If
463 Return MyBase.CanConvertTo(context, destinationType)
464 End Function
465
466 **//C#** public override bool CanConvertTo(ITypeDescriptorContext context,
467 System.Type destinationType)
468 {
469 if (destinationType == typeof(SpellingOptions))
470 return true;
471
472 return base.CanConvertTo(context, destinationType);
473 }
474
475 3. 覆盖 **ConvertTo** 方法,并确保 ` destinationType ` 参数是一个 **String** ,并且值的类型与使用此类型转换器的类(示例中的 ` SpellingOptions ` 类)相同。如果其中任一情况为 **false** ,都将返回基类 **ConvertTo** 方法的值;否则,返回值对象的字符串表示。字符串表示需要使用唯一分隔符将类的每个属性隔开。由于整个字符串都将显示在 **PropertyGrid** 中,因此需要选择一个不会影响可读性的分隔符,逗号的效果通常比较好。
476
477 **' Visual Basic** Public Overloads Overrides Function ConvertTo( _
478 ByVal context As ITypeDescriptorContext, _
479 ByVal culture As CultureInfo, _
480 ByVal value As Object, _
481 ByVal destinationType As System.Type) _
482 As Object
483 If (destinationType Is GetType(System.String) _
484 AndAlso TypeOf value Is SpellingOptions) Then
485
486 Dim so As SpellingOptions = CType(value, SpellingOptions)
487
488 Return "在键入时检查: " & so.SpellCheckWhileTyping & _
489 ",检查大小写: " & so.SpellCheckCAPS & _
490 ",建议更正: " & so.SuggestCorrections
491 End If
492 Return MyBase.ConvertTo(context, culture, value, destinationType)
493 End Function
494
495 **//C#** public override object ConvertTo(ITypeDescriptorContext context,
496 CultureInfo culture,
497 object value,
498 System.Type destinationType)
499 {
500 if (destinationType == typeof(System.String) &&
501 value is SpellingOptions){
502
503 SpellingOptions so = (SpellingOptions)value;
504
505 return "在键入时检查:" + so.SpellCheckWhileTyping +
506 ",检查大小写: " + so.SpellCheckCAPS +
507 ",建议更正: " + so.SuggestCorrections;
508 }
509 return base.ConvertTo(context, culture, value, destinationType);
510 }
511
512 4. (可选)通过指定类型转换器可以从字符串进行转换,您可以启用网格中对象字符串表示的编辑。要执行此操作,首先需要覆盖 **CanConvertFrom** 方法并返回 **true** (如果源 **Type** 参数为 **String** 类型);否则,返回基类 **CanConvertFrom** 方法的值。
513
514 **' Visual Basic** Public Overloads Overrides Function CanConvertFrom( _
515 ByVal context As ITypeDescriptorContext, _
516 ByVal sourceType As System.Type) As Boolean
517 If (sourceType Is GetType(String)) Then
518 Return True
519 End If
520 Return MyBase.CanConvertFrom(context, sourceType)
521 End Function
522
523 **//C#** public override bool CanConvertFrom(ITypeDescriptorContext context,
524 System.Type sourceType)
525 {
526 if (sourceType == typeof(string))
527 return true;
528
529 return base.CanConvertFrom(context, sourceType);
530 }
531
532 5. 要启用对象基类的编辑,同样需要覆盖 **ConvertFrom** 方法并确保值参数是一个 **String** 。如果不是 **String** ,将返回基类 **ConvertFrom** 方法的值;否则,返回基于值参数的类(示例中的 ` SpellingOptions ` 类)的新实例。您需要根据值参数解析类的每个属性的值。了解在 **ConvertTo** 方法中创建的分隔字符串的格式将有助于您的解析。
533
534 **' Visual Basic** Public Overloads Overrides Function ConvertFrom( _
535 ByVal context As ITypeDescriptorContext, _
536 ByVal culture As CultureInfo, _
537 ByVal value As Object) As Object
538
539 If (TypeOf value Is String) Then
540 Try
541 Dim s As String = CStr(value)
542 Dim colon As Integer = s.IndexOf(":")
543 Dim comma As Integer = s.IndexOf(",")
544
545 If (colon <> -1 AndAlso comma <> -1) Then
546 Dim checkWhileTyping As String = s.Substring(colon + 1, _
547 (comma - colon - 1))
548
549 colon = s.IndexOf(":", comma + 1)
550 comma = s.IndexOf(",", comma + 1)
551
552 Dim checkCaps As String = s.Substring(colon + 1, _
553 (comma - colon - 1))
554
555 colon = s.IndexOf(":", comma + 1)
556
557 Dim suggCorr As String = s.Substring(colon + 1)
558
559 Dim so As SpellingOptions = New SpellingOptions()
560
561 so.SpellCheckWhileTyping = Boolean.Parse(checkWhileTyping)
562 so.SpellCheckCAPS = Boolean.Parse(checkCaps)
563 so.SuggestCorrections = Boolean.Parse(suggCorr)
564
565 Return so
566 End If
567 Catch
568 Throw New ArgumentException( _
569 "无法将“" & CStr(value) & _
570 "”转换为 SpellingOptions 类型")
571
572 End Try
573 End If
574 Return MyBase.ConvertFrom(context, culture, value)
575 End Function
576
577 **//C#** public override object ConvertFrom(ITypeDescriptorContext context,
578 CultureInfo culture, object value)
579 {
580 if (value is string) {
581 try {
582 string s = (string) value;
583 int colon = s.IndexOf(':');
584 int comma = s.IndexOf(',');
585
586 if (colon != -1 && comma != -1) {
587 string checkWhileTyping = s.Substring(colon + 1 ,
588 (comma - colon - 1));
589
590 colon = s.IndexOf(':', comma + 1);
591 comma = s.IndexOf(',', comma + 1);
592
593 string checkCaps = s.Substring(colon + 1 ,
594 (comma - colon -1));
595
596 colon = s.IndexOf(':', comma + 1);
597
598 string suggCorr = s.Substring(colon + 1);
599
600 SpellingOptions so = new SpellingOptions();
601
602 so.SpellCheckWhileTyping =Boolean.Parse(checkWhileTyping);
603 so.SpellCheckCAPS = Boolean.Parse(checkCaps);
604 so.SuggestCorrections = Boolean.Parse(suggCorr);
605
606 return so;
607 }
608 }
609 catch {
610 throw new ArgumentException(
611 "无法将“" + (string)value +
612 "”转换为 SpellingOptions 类型");
613 }
614 }
615 return base.ConvertFrom(context, culture, value);
616 }
617
618 6. 现在已经有了一个类型转换器类,下面您需要确定使用该类的目标类。您可以通过将 **TypeConverterAttribute** 应用到目标类(示例中的 ` SpellingOptions ` 类)来执行此操作。
619
620 **' Visual Basic** ' 应用于 SpellingOptions 类的 TypeConverter 特性。
621 < ** **TypeConverter(GetType(SpellingOptionsConverter)),** _**
622 DescriptionAttribute("展开以查看应用程序的拼写选项。")> _
623 Public Class SpellingOptions
624 ...
625 End Class
626
627
628 **//C#**
629
630 // 应用于 SpellingOptions 类的 TypeConverter 特性。
631 [ **TypeConverterAttribute(typeof(SpellingOptionsConverter)),**
632 DescriptionAttribute("展开以查看应用程序的拼写选项。")]
633 public class SpellingOptions{ ... }
634
635
636
637
638再次编译并运行选项窗口应用程序。下面的屏幕快照显示了选项窗口目前的外观。
639
640`  `
641
642**图 6:在 PropertyGrid 中显示的带有类型转换器的自定义数据类型**
643
644> ****注意:** ` ` ** 如果只需要可展开对象支持,而不需要自定义字符串表示,则只需将 **TypeConverterAttribute** 应用到类中。将 **ExpandableObjectConverter** 指定为类型转换器类型。
645
646#### 添加域列表和简单的下拉列表属性支持
647
648对于基于 **Enum** 类型返回枚举的属性, **PropertyGrid** 会自动在下拉列表中显示枚举值。 **EnumConverter** 也提供了这一功能。对于自己的属性,您可能希望为用户提供一个有效值列表(有时也称为选取列表或域列表),而其类型并不是基于 **Enum** 。如果域值在运行时之前未知,或者值可以更改,则属于这种情况。
649
650修改选项窗口,提供一个用户可从中选择的默认文件名的域列表。您已经将 ` DefaultFileName ` 属性添加到 ` AppSettings ` 类。下一步是在 **PropertyGrid** 中显示属性的下拉列表,以提供域列表。
651
652#### 提供简单的下拉列表属性支持
653
654 1. 创建一个从类型转换器类继承而来的类。由于 ` DefaultFileName ` 属性属于 **String** 类型,因此可以从 **StringConverter** 中继承。如果属性类型的类型转换器不存在,则可以从 **TypeConverter** 继承;这里并不需要。
655
656 **' Visual Basic** Public Class FileNameConverter
657 Inherits StringConverter
658 End Class
659
660
661 **//C#**
662
663 public class FileNameConverter: StringConverter
664 { }
665
666 2. 覆盖 **GetStandardValuesSupported** 方法并返回 **true** ,表示此对象支持可以从列表中选取的一组标准值。
667
668 **' Visual Basic** Public Overloads Overrides Function GetStandardValuesSupported( _
669 ByVal context As ITypeDescriptorContext) As Boolean
670 Return True
671 End Function
672
673
674 **//C#**
675
676 public override bool GetStandardValuesSupported(
677 ITypeDescriptorContext context)
678 {
679 return true;
680 }
681
682 3. 覆盖 **GetStandardValues** 方法并返回填充了标准值的 **StandardValuesCollection** 。创建 **StandardValuesCollection** 的方法之一是在构造函数中提供一个值数组。对于选项窗口应用程序,您可以使用填充了建议的默认文件名的 **String** 数组。
683
684 **' Visual Basic** Public Overloads Overrides Function GetStandardValues( _
685 ByVal context As ITypeDescriptorContext) _
686 As StandardValuesCollection
687
688 Return New StandardValuesCollection(New String() {"新文件", _
689 "文件1", _
690 "文档1"})
691 End Function
692
693
694 **//C#**
695
696 public override StandardValuesCollection
697 GetStandardValues(ITypeDescriptorContext context)
698 {
699 return new StandardValuesCollection(new string[]{"新文件",
700 "文件1",
701 "文档1"});
702 }
703
704 4. (可选)如果希望用户能够键入下拉列表中没有包含的值,请覆盖 **GetStandardValuesExclusive** 方法并返回 **false** 。这从根本上将下拉列表样式变成了组合框样式。
705
706 **' Visual Basic** Public Overloads Overrides Function GetStandardValuesExclusive( _
707 ByVal context As ITypeDescriptorContext) As Boolean
708 Return False
709 End Function
710
711
712 **//C#**
713
714 public override bool GetStandardValuesExclusive(
715 ITypeDescriptorContext context)
716 {
717 return false;
718 }
719
720 5. 拥有自己的用于显示下拉列表的类型转换器类后,您需要确定使用该类的目标。在本示例中,目标为 ` DefaultFileName ` 属性,因为类型转换器是针对该属性的。将 **TypeConverterAttribute** 应用到目标属性中。
721
722 **' Visual Basic** ' 应用到 DefaultFileName 属性的 TypeConverter 特性。
723 < ** **TypeConverter(GetType(FileNameConverter)),** **_****
724 CategoryAttribute("文档设置")> _
725 Public Property DefaultFileName() As String
726 Get
727 Return _defaultFileName
728 End Get
729 Set(ByVal Value As String)
730 _defaultFileName = Value
731 End Set
732 End Property
733
734
735 **//C#**
736
737 // 应用到 DefaultFileName 属性的 TypeConverter 特性。
738 [ ** **TypeConverter(typeof(FileNameConverter)),** **
739 CategoryAttribute("文档设置")]
740 public string DefaultFileName
741 {
742 get{ return defaultFileName; }
743 set{ defaultFileName = value; }
744 }
745
746
747
748
749再次编译并运行选项窗口应用程序。下面的屏幕快照显示了选项窗口目前的外观。请注意 ` DefaultFileName ` 属性的外观。
750
751`  `
752
753**图 7:在 PropertyGrid 中显示下拉域列表**
754
755## 为属性提供自定义 UI
756
757如上所述,.NET 框架类型使用 **TypeConverter** 和 **UITypeEditor** 类(以及其他类)来提供 **PropertyGrid** 编辑支持。有关如何使用 **TypeConverter** ,请参阅 对自定义类型的支持 一节;您也可以使用 **UITypeEditor** 类来自定义 **PropertyGrid** 。
758
759您可以在 **PropertyGrid** 中提供小图形表示和属性值,类似于为 **Image** 和 **Color** 类提供的内容。要在自定义中执行此操作,请从 **UITypeEditor** 继承,覆盖 **GetPaintValueSupported** 并返回 **true** 。然后,覆盖 **UITypeEditor.PaintValue** 方法,并在自己的方法中使用 **PaintValueEventArgs.Graphics** 参数绘制图形。最后,将 **Editor** 特性应用到使用 **UITypeEditor** 类的类或属性。
760
761下面的屏幕快照显示了结果外观。
762
763`  `
764
765**图 8:在 PropertyGrid 中显示属性的自定义图形**
766
767您也可以提供自己的下拉列表控件,这与 **Control.Dock** 属性用来为用户提供靠接选择的控件类似。要执行此操作,请从 **UITypeEditor** 继承,覆盖 **GetEditStyle** ,然后返回一个 **UITypeEditorEditStyle** 枚举值,例如 **DropDown** 。您的自定义下拉列表控件必须从 **Control** 或 **Control** 的派生类(例如 **UserControl** )继承而来。然后,覆盖 **UITypeEditor.EditValue** 方法。使用 **IServiceProvider** 参数调用 **IServiceProvider.GetService** 方法,以获取一个 **IWindowsFormsEditorService** 实例。最后,调用 **IWindowsFormsEditorService.DropDownControl** 方法来显示您的自定义下拉列表控件。请记住将 **Editor** 特性应用到使用 **UITypeEditor** 类的类或属性中。
768
769下面的屏幕快照显示了结果外观。
770
771`  `
772
773**图 9:在 PropertyGrid 中显示属性的自定义下拉列表控件**
774
775除了使用 **TypeEditor** 和 **UITypeEditor** 类外,还可以自定义 **PropertyGrid** 以显示其他属性选项卡。属性选项卡从 **PropertyTab** 类继承而来。如果您使用过 Microsoft Visual C#™ .NET 中的属性浏览器,那么就可能看到过自定义的 **PropertyTab** 。 **Events** 选项卡(带有闪电图形的按钮)就是一个自定义的 **PropertyTab** 。下面的屏幕快照显示了自定义 **PropertyTab** 的另一个示例。可以使用 **PropertyTab** 编辑按钮的边界点,以创建自定义的按钮形状。
776
777`  `
778
779**图 10:在 PropertyGrid 中显示自定义选项卡**
780
781有关使用 **UITypeEditor** 类自定义 **PropertyGrid** 的详细信息,以及上述自定义用户界面代码示例,请参阅 Shawn Burke 的文章 Make Your Components Really RAD with Visual Studio .NET Property Browser (英文)。
782
783## 小结
784
785.NET 框架提供的 **ProperyGrid** 控件具有丰富的编辑功能,您可以使用这些编辑功能来改善您的用户界面。 **PropertyGrid** 的自定义非常简单,您可以在任何应用程序中使用这一控件。此外,由于 Visual Studio .NET 属性浏览器是建立在 **PropertyGrid** 的基础之上的,因此您可以使用这些技术提供更丰富的设计时体验。</defaultvalueattribute(true)></defaultvalueattribute(false)></defaultvalueattribute(true)></descriptionattribute("展开以查看应用程序的拼写选项。")></categoryattribute("版本"),></browsableattribute(false),></descriptionattribute("以毫秒表示的文本重复率。"),></categoryattribute("全局设置"),></categoryattribute("全局设置"),></categoryattribute("全局设置")></categoryattribute("文档设置")></categoryattribute("文档设置")></categoryattribute("文档设置"),></defaultpropertyattribute("saveonclose")></categoryattribute("版本"),></browsableattribute(false),**></descriptionattribute("以毫秒表示的文本重复率。"),></categoryattribute("全局设置"),></categoryattribute("全局设置"),></categoryattribute("文档设置"),>