Custom Controls and Design-Time Support: Part 2/2 - Designer Verbs
(Page 4 of 7 )
You can also use a custom designer to add to the context menu that's displayed when a programmer right-clicks on your control in the design environment. This menu contains some standard options provided by Visual Studio .NET, but it can also contain your own commands (technically known as verbs).
To add your own verbs, you need to override the Verbs property, create a new DesignerVerbCollection, and add the appropriate DesignerVerb object entries. Your control designer handles the verb, generally by updating the associated control.
The following example retrieves a list of all the drives on the current computer, and adds a context menu entry for each one. The user can click on the appropriate entry to set the Drive property of the control.
public class DirectoryTreeDesigner : ControlDesigner
{
protected override void PostFilterProperties(
System.Collections.IDictionary properties)
{
properties.Remove("Nodes");
}
DesignerVerbCollection verbs = new DesignerVerbCollection();
public DirectoryTreeDesigner()
{
// Configure the designer verb collection.
string[] drives = System.IO.Directory.GetLogicalDrives();
foreach (string drive in drives)
{
verbs.Add(new DesignerVerb("Set Drive " + drive,
new EventHandler(OnVerb)));
}
}
public override DesignerVerbCollection Verbs
{
get
{ return verbs; }
}
protected void OnVerb(object sender, EventArgs e)
{
// Retrieve the selected drive.
char driveLetter = ((DesignerVerb)sender).Text[10];
// Adjust the associated control.
((DirectoryTree)this.Control).Drive = driveLetter;
}
} Generally you won't use your designer verbs to provide settings for a property. A more interesting technique is to provide higher-level configuration operations that adjust several properties at once.
One example of this is found in the ASP.NET Calendar control, which allows the user to choose a theme from a list of preset choices. When a theme is selected, several properties are modified in conjunction.
The implementation for this design is refreshing easy. Just add a Windows form to your project, and display it when the appropriate designer verb is selected. Here's another simple example using the DirectoryTree. This time, only a single verb is available, which then displays a window that allows the user to choose a drive.
When a drive is chosen, a public form-level variable is set, and then retrieved by the designer, which applies the change. This approach is more manageable than the previous design, and doesn't clutter the context menu with drive letters:
public class DirectoryTreeDesigner : ControlDesigner
{
protected override void PostFilterProperties(
System.Collections.IDictionary properties)
{
properties.Remove("Nodes");
}
DesignerVerbCollection verbs = new DesignerVerbCollection();
public DirectoryTreeDesigner()
{
// Configure the designer verb collection.
verbs.Add(new DesignerVerb("Set Drive",
new EventHandler(OnVerb)));
}
public override DesignerVerbCollection Verbs
{
get
{ return verbs; }
}
protected void OnVerb(object sender, EventArgs e)
{
// Show the form.
SelectDrive frm = new SelectDrive();
frm.DriveSelection = ((DirectoryTree)this.Control).Drive;
frm.ShowDialog();
// Adjust the associated control.
((DirectoryTree)this.Control).Drive = frm.DriveSelection;
}
} The SelectDrive form is quite simple:
public class SelectDrive : System.Windows.Forms.Form
{
private System.Windows.Forms.Button cmdClose;
private System.Windows.Forms.ListBox lstDrives;
private void cmdClose_Click(object sender, System.EventArgs e)
{
this.Close();
}
public char DriveSelection;
private void listDrives_SelectedIndexChanged(object sender,
System.EventArgs e)
{
DriveSelection = lstDrives.Text[0];
}
private void SelectDrive_Load(object sender, System.EventArgs e)
{
string[] drives = System.IO.Directory.GetLogicalDrives();
lstDrives.DataSource = drives;
// Select the current drive.
lstDrives.SelectedIndex =
lstDrives.FindString(DriveSelection.ToString());
// Attach the event handler.
// This step is performed after the selected index is set,
// to prevent it from being overwritten as the list is built.
this.lstDrives.SelectedIndexChanged += new
System.EventHandler(this.listBox1_SelectedIndexChanged);
}
} One quirk remains in the designer that we've built. When the DirectoryTree.Drive property is modified by the designer, the Properties window is not updated until the control is deselected and then reselected.
To correct this defect, you need to explicitly notify the IDE that a change has been made.
The rewritten OnVerb() method handles this:
// Notify the IDE that the Drive property has changed.
PropertyDescriptorCollection properties;
properties = TypeDescriptor.GetProperties(typeof(DirectoryTree));
PropertyDescriptor changedProperty = properties.Find("Drive", false);
this.RaiseComponentChanged(changedProperty, "", frm.DriveSelection); When you add a form to a control project in this way, the client will be able to see the form class, and even create and display instances of it. If this isn't the behavior you want, you will need to nest your form class inside your control class and make it private or protected.
Unfortunately, if you do this, you have to forego Visual Studio .NET's design-time support, and manually copy the form code into the class.
Next: UITypeEditors >>
More C# Articles
More By Wrox Team