All that is required in order to associate the
UITypeEditor
with a particular property is the use of the
EditorAttribute
. The
EditorAttribute
specifies the type of editor to be used when editing the property at design-time. Figure 5.7 shows the
UITypeEditor
that will be built for the
IconButton
's
Icon
property.
The typical coding convention for
Modal
style editors is to have the
Form
class contained within the editor class as a nested class. The drawback to this is that the designer will not allow for visual development of a nested class. When developing the
UITypeEditor
, you may want to create the
Form
class as you would any other
Form
and then, when finished testing, move the
Form
class into the editor class. This, however, creates a small issue with the resource file, the corresponding file with the
resx
extension. To preserve the resource information, it is necessary to include the resource file into the project. I suggest the following steps for creating nested
Form
classes:
-
Create a new
Form
class.
-
When you are finished developing the form, copy the underlying
resx
file somewhere safe.
-
Copy the
Form
class code into the target parent class, in this case the
IconEditor
class.
-
Delete the
Form
class file.
-
Add the saved
resx
file to the project as a resource. Adding an existing item and choosing the
resx
file can do this.
-
Double-click the resource file in the Solution explorer. This invokes the resource editor.
-
In the data sheet of the resource editor, modify the value of
$this.
name
entry to be the qualified name of the dialog (see Figure 5.8). In the case of the
IconEditorDialog
the qualified name is
IconEditor.IconEditorDialog
.
-
Locate the line of code in the
InitializeComponent
method of the form where the resource manager is referenced. It is usually the first line. Modify the qualified name so that it matches that of the
$this.name
value in the resource file. The following snippet shows the line of code to modify:
System.Resources.ResourceManager resources = new
System.Resources.ResourceManager(typeof(IconEditor.IconEditorDialog));
-
You are ready to build the project.
If it seems like a complex set of steps, it is. Unfortunately, the resource file support for VS .NET is not quite up to par with the way it should be in terms of ease of use. In an attempt to simplify the development process, certain
tasks
such as resource file management have been made overly complex.
Listing 5.2 contains the source for the
IconEditor
, along with the nested
IconEditorDialog
class to act as a custom
UITypeEditor
.
Listing 5.2
IconEditor
UITypeEditor
Source
1: using System;
2: using System.Drawing;
3: using System.Drawing.Design;
4: using System.Collections;
5: using System.ComponentModel;
6: using System.Windows.Forms;
7: using System.Windows.Forms.Design;
8:
9:
10: namespace SAMS.ToolKit.Design
11: {
12:
13: public class IconEditor : System.Drawing.Design.UITypeEditor
14: {
15:
16: public override object EditValue(ITypeDescriptorContext context,
17: IServiceProvider provider,
18: object value) {
19: IconEditorDialog dlg = new IconEditorDialog( );
20: IWindowsFormsEditorService winFormEditorService =
(IWindowsFormsEditorService)provider.GetService( typeof( IWindowsFormsEditorService ) );
21: if( DialogResult.OK == winFormEditorService.ShowDialog(dlg))
22: value = dlg.SelectedIcon;
23:
24: return value;
25: }
26:
27: public override UITypeEditorEditStyle GetEditStyle( ITypeDescriptorContext context
) {
28: return UITypeEditorEditStyle.Modal;
29: }
30:
31: public override bool GetPaintValueSupported( ITypeDescriptorContext context)
{
32: return true;
33: }
34:
35: public override void PaintValue( System.Drawing.Design.PaintValueEventArgs
e) {
36: if( !(e.Value is Icon) )
37: return;
38: Image img = ((Icon)e.Value).ToBitmap( );
39: Rectangle rcBounds = e.Bounds;
40: rcBounds.Inflate(-1,-1);
41: Pen p = System.Drawing.SystemPens.WindowFrame;
42: e.Graphics.DrawRectangle(p, rcBounds );
43: if( img != null )
44: e.Graphics.DrawImage( img, e.Bounds );
45: }
46:
47:
48: protected class IconEditorDialog : System.Windows.Forms.Form
49: {
50: private System.Windows.Forms.PictureBox iconPreviewPictureBox;
51: private System.Windows.Forms.Button btnOpen;
52: private System.Windows.Forms.Button btnOK;
53: private System.Windows.Forms.Button btnCancel;
54: private System.Windows.Forms.PictureBox samsImagePictureBox;
55: private System.Windows.Forms.OpenFileDialog openFileDialog;
56:
57: private System.ComponentModel.Container components = null;
58:
59: private System.Drawing.Icon selectedIcon;
60:
61: public Icon SelectedIcon
62: {
63: get { return selectedIcon; }
64: set
65: {
66: selectedIcon = value;
67: iconPreviewPictureBox.Image = ( selectedIcon != null ?
selectedIcon.ToBitmap( ) : null );
68: }
69: }
70:
71:
72: public IconEditorDialog()
73: {
74: InitializeComponent();
75: }
76:
77: /// <summary>
78: /// Clean up any resources being used.
79: /// </summary>
80: protected override void Dispose( bool disposing )
81: {
82: if( disposing )
83: {
84: if (components != null)
85: {
86: components.Dispose();
87: }
88: }
89: base.Dispose( disposing );
90: }
91:
92: private void InitializeComponent()
93: {
94: System.Resources.ResourceManager resources = new
System.Resources.ResourceManager(typeof(IconEditor.IconEditorDialog));
95: this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
96: this.btnCancel = new System.Windows.Forms.Button();
97: this.iconPreviewPictureBox = new System.Windows.Forms.PictureBox();
98: this.btnOpen = new System.Windows.Forms.Button();
99: this.samsImagePictureBox = new System.Windows.Forms.PictureBox();
100: this.btnOK = new System.Windows.Forms.Button();
101: this.SuspendLayout();
102: //
103: // openFileDialog
104: //
105: this.openFileDialog.Filter = "Icon Files *.ico";
106: //
107: // btnCancel
108: //
109: this.btnCancel.Location = new System.Drawing.Point(120, 88);
110: this.btnCancel.Name = "btnCancel";
111: this.btnCancel.Size = new System.Drawing.Size(88, 24);
112: this.btnCancel.TabIndex = 2;
113: this.btnCancel.Text = "Cancel";
114: this.btnCancel.Click += new
System.EventHandler(this.btnCancel_Click);
115: //
116: // iconPreviewPictureBox
117: //
118: this.iconPreviewPictureBox.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D;
119: this.iconPreviewPictureBox.Location = new System.Drawing.Point(8, 8);
120: this.iconPreviewPictureBox.Name = "iconPreviewPictureBox";
121: this.iconPreviewPictureBox.Size = new System.Drawing.Size(100, 68);
122: this.iconPreviewPictureBox.SizeMode =
System.Windows.Forms.PictureBoxSizeMode.CenterImage;
123: this.iconPreviewPictureBox.TabIndex = 1;
124: this.iconPreviewPictureBox.TabStop = false;
125: //
126: // btnOpen
127: //
128: this.btnOpen.Location = new System.Drawing.Point(120, 8);
129: this.btnOpen.Name = "btnOpen";
130: this.btnOpen.Size = new System.Drawing.Size(88, 24);
131: this.btnOpen.TabIndex = 2;
132: this.btnOpen.Text = "Open...";
133: this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
134: //
135: // samsImagePictureBox
136: //
137: this.samsImagePictureBox.Image =
((System.Drawing.Bitmap)(resources.GetObject( "samsImagePictureBox.Image")));
138: this.samsImagePictureBox.Location = new System.Drawing.Point(8, 88);
139: this.samsImagePictureBox.Name = "samsImagePictureBox";
140: this.samsImagePictureBox.Size = new System.Drawing.Size(96, 32);
141: this.samsImagePictureBox.SizeMode =
System.Windows.Forms.PictureBoxSizeMode.CenterImage;
142: this.samsImagePictureBox.TabIndex = 3;
143: this.samsImagePictureBox.TabStop = false;
144: //
145: // btnOK
146: //
147: this.btnOK.Location = new System.Drawing.Point(120, 48);
148: this.btnOK.Name = "btnOK";
149: this.btnOK.Size = new System.Drawing.Size(88, 24);
150: this.btnOK.TabIndex = 2;
151: this.btnOK.Text = "OK";
152: this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
153: //
154: // IconEditorDialog
155: //
156: this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
157: this.ClientSize = new System.Drawing.Size(228, 129);
158: this.Controls.AddRange(new System.Windows.Forms.Control[]{
159: this.samsImagePictureBox,
160: this.btnCancel,
161: this.btnOK,
162: this.btnOpen,
163: this.iconPreviewPictureBox} );
164: this.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.FixedDialog;
165: this.Name = "IconEditorDialog";
166: this.Text = "IconButton UITypeEditor";
167: this.ResumeLayout(false);
168:
169: }
170:
171: private void btnOpen_Click(object sender, System.EventArgs e)
172: {
173: if( DialogResult.OK == openFileDialog.ShowDialog( ) ) {
174: try {
175: selectedIcon = new Icon( openFileDialog.FileName );
176: } catch( Exception exception ) {
177: MessageBox.Show( this, exception.Message );
178: } finally {
179: iconPreviewPictureBox.Image = ( selectedIcon != null ?
selectedIcon.ToBitmap( ) : null );
180: }
181: }
182: }
183:
184: private void btnOK_Click(object sender, System.EventArgs e)
185: {
186: DialogResult = DialogResult.OK;
187: Close( );
188: }
189:
190: private void btnCancel_Click(object sender, System.EventArgs e)
191: {
192: DialogResult = DialogResult.Cancel;
193: Close( );
194: }
195: }
196: }
197: }
The code in Listing 5.2 consists of two major components: the dialog for selecting and previewing the icon image, and the code necessary to paint the icon image within the
PropertyGrid
.
The nested
IconEditorDialog
is a basic
Form
-derived class that is used to display an
OpenFileDialog
for selecting an icon. In addition, the
IconEditDialog
creates an instance of an
IconButton
to allow for a simple preview mechanism. After an icon is selected, the
IconEditDialog
is closed and the
IconEditor
class is responsible for providing the necessary code to update the
PropertyGrid
.
The
IconEditor
class provides the implementation necessary to interact with the
PropertyGrid
. The first task is to return the type of editor that will be provided. For the
IconEditor
, the type of
Modal
is returned, as a nested
Form
will be used. Other types are
DropDown
and
None
. When the
EditValue
method is invoked, the
IconEditor
creates and displays the nested
IconEditorDialog
to allow for the selection of an icon image. When the value of the icon is modified, the
PropertyGrid
will invoke the
GetPaintValueSupported
method to determine whether the editor will handle displaying some representation of the property within the
PropertyGrid
.
Next, the
PaintValue
method is invoked if the
GetPaintValueSupported
method returns
true
. The
PaintValue
method is responsible for painting within the
PropertyGrid
for the associated property. The
IconEditor
renders
a
miniature
image of the icon within0 the
PropertyGrid
.
To associate the
IconEditor
with the
Icon
property, modify the
IconButton
source and add the
EditorAttribute
to the
Icon
property as shown in Listing 5.3.
Listing 5.3 Applying the
EditorAttribute
1: [
2: System.ComponentModel.Description("The Icon to be displayed in the button"),
3: System.ComponentModel.Category( "Appearance" ),
4: System.ComponentModel.DefaultValue( null ),
5: System.ComponentModel.Editor( typeof( SAMS.ToolKit.Design.IconEditor ), typeof(
System.Drawing.Design.UITypeEditor ) )
6: ]
7: public Icon Icon { /* omitted */ }