Introduction to Designers

Divil

Well-known member
Joined
Nov 17, 2002
Messages
2,748
What are Designers?

Designers are the gateway between your user control and the developer at design time. Any control present on the design surface is fully compiled and sitting on a form just like it would be at runtime, only it has a designer applied to it.

How do they Work?

Designers subclass controls and only allow certain messages to get through. When you click on a button on a form at design time, for example, the button never receives the click message. Instead, the designer processes the message and the control is selected, just as you would expect.

There are a few kinds of designer that you may come across, but they all implement the IDesigner interface. This fairly simplistic interface defines only four members, but in reality when you implement a designer you will inherit from either ComponentDesigner or ControlDesigner.

What can I do with a Designer?

Some pretty cool stuff. If you enjoy writing controls as I do, designers are a godsend. Im not exaggerating when I say that more than half the time spent developing a control for .net can be spent developing the design-time experience. In most cases you can get away without writing one at all, but having a designer for your control or component helps give it that professional polish that people expect from good controls.

An example of good designer usage is the .net Tab Control. This familiar control uses a set of tabs to switch between several client areas, thus saving space on forms where a lot of information is needed. The TabControl itself has a designer, which allows the user to switch the visible tab at design time. Normally click messages wouldnt get through to the underlying control at design time, but with the aid of a designer this is possible.

The TabControl class adds TabPage instances to itself to host child controls. A TabPage doesnt do a lot more than inherit from ContainerControl so you can host controls on it. Or does it? In actual fact, the TabPage Designer class does more. For instance, it stops the user from being able to move the TabPage. It should stay stationary and be moved and sized by its parent TabControl, and the designer enforces that. Also, the designer listens for when the user selects the TabPage and deletes it, and notifies the parent TabControl so it can update its collection and interface.

Another example of designer usage is the TextBox control. Have you ever wondered how it stops the user from resizing it when its multiline property is set to False? Well, you probably havent, but its with a designer, and its remarkably easy to achieve.

All the selecting, moving and resizing of controls you do at design time is handled by the designers that are part of the framework. Together with a bucket load of interfaces implemented by the host environment, you can create an extremely flexible and intuitive design time experience for a developer using one of your controls.

Writing a Designer

As this article is only meant to be a brief introduction to designers, we wont make anything terribly complicated. Before we can make a designer we need a control to design, so well create a simple usercontrol that draws an ellipse in its client area.

To create a custom designer for a control you will either inherit from ControlDesigner or ParentControlDesigner, depending on whether you want users to be able to place child controls in your control. All we need for this article is to inherit from ControlDesigner. You will need to add an assembly reference to System.Design.dll to see these classes. To associate your designer class with your control, you use the DesignerAttribute class:

Code:
Imports System.Windows.Forms.Design
Imports System.ComponentModel

<Designer(GetType(MyControlDesigner))> _
Public Class UserControl1
    Inherits System.Windows.Forms.UserControl

    Public Sub New()
        MyBase.New()

        SetStyle(ControlStyles.ResizeRedraw, True)
        SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        SetStyle(ControlStyles.DoubleBuffer, True)
    End Sub

    Private Sub UserControl1_Paint(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        e.Graphics.DrawEllipse(Pens.Red, ClientRectangle)
    End Sub
End Class

Friend Class MyControlDesigner
    Inherits ControlDesigner

End Class

C#:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace CSharpTest
{
	[Designer(typeof(MyControlDesigner))]
	public class UserControl1 : System.Windows.Forms.UserControl
	{
		public UserControl1()
		{
			SetStyle(ControlStyles.ResizeRedraw, true);
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			SetStyle(ControlStyles.DoubleBuffer, true);

			this.Paint += new PaintEventHandler(UserControl1_Paint);
		}

		private void UserControl1_Paint(object sender, PaintEventArgs e)
		{
			e.Graphics.DrawEllipse(Pens.Red, ClientRectangle);
		}
	}

	internal class MyControlDesigner : System.Windows.Forms.Design.ControlDesigner
	{

	}
}

In the Form designer, you should see the usercontrol you just created in the toolbox, and you can drag it on to the form like you would any other control. Although our designer class doesnt do anything yet, the instance on the form is using it.

Now lets try preventing the user from resizing the control vertically, like the TextBox does. To do this you need to override the SelectionRules property of the ControlDesigner class, and return all flags for sizing and moving except the Top and Bottom ones:

Code:
Friend Class MyControlDesigner
    Inherits ControlDesigner

    Public Overrides ReadOnly Property SelectionRules() As System.Windows.Forms.Design.SelectionRules
        Get
            Return SelectionRules.LeftSizeable Or SelectionRules.RightSizeable Or _
            SelectionRules.Moveable Or SelectionRules.Visible
        End Get
    End Property
End Class

C#:
public override System.Windows.Forms.Design.SelectionRules SelectionRules
{
	get
	{
		return System.Windows.Forms.Design.SelectionRules.LeftSizeable |
			System.Windows.Forms.Design.SelectionRules.RightSizeable |
			System.Windows.Forms.Design.SelectionRules.Visible |
			System.Windows.Forms.Design.SelectionRules.Moveable;
	}
}

Now, if you create an instance of your control on the form, youll notice that the designer has disabled all sizing grips that would allow you to size it vertically.

Next, well expand our designer to illustrate another cool feature; choosing what controls our control can be parented to. We will make it so that our control cannot be parented to Panel controls, but everything else will be ok. This may seem like a fruitless exercise, but it demonstrates a technique not uncommon when writing designers. All we have to do is override the CanBeParentedTo function and see if the potential parent designer is hosting a control of type Panel:

Code:
Public Overrides Function CanBeParentedTo(ByVal parentDesigner As System.ComponentModel.Design.IDesigner) _
As Boolean
    If TypeOf parentDesigner.Component Is Panel Then
        Return False
    Else
        Return True
    End If
End Function

C#:
public override bool CanBeParentedTo(System.ComponentModel.Design.IDesigner parentDesigner)
{
	if (parentDesigner.Component is Panel)
		return false;
	else
		return true;
}

You will now find that your control can be dragged in to most parent controls, like the GroupBox for example, but not Panel controls.

The last thing I will cover in this article is designer verbs. These are actions that are presented on the controls context menu and also as hyperlinks in the property grid when your component is selected, and allow you to provide an interface for the user for performing common tasks with your control.

To implement designer verbs, you must override the Verbs property and return a DesignerVerbCollection object. Each designer verb is presented as a menu option and as a hyperlink in the property grid, and is associated with an event handler that is called when it is selected. Lets try adding one that simply shows a message box when chosen.

Code:
Imports System.ComponentModel.Design

Public Overrides ReadOnly Property Verbs() As System.ComponentModel.Design.DesignerVerbCollection
    Get
        Dim v As New DesignerVerbCollection()

        v.Add(New DesignerVerb("Sample Verb", AddressOf SampleVerbHandler))
        Return v
    End Get
End Property

Private Sub SampleVerbHandler(ByVal sender As Object, ByVal e As System.EventArgs)
    MessageBox.Show("You clicked the test designer verb!")
End Sub

C#:
using System.ComponentModel.Design;

public override System.ComponentModel.Design.DesignerVerbCollection Verbs
{
	get
	{
		DesignerVerbCollection v = new DesignerVerbCollection();

		v.Add(new DesignerVerb("Sample Verb", new EventHandler(SampleVerbHandler)));
		return v;
	}
}

private void SampleVerbHandler(object sender, System.EventArgs e)
{
	MessageBox.Show("You clicked the test designer verb!");
}

When your control is selected on the design surface, our hyperlink is now shown in the property grid.

Conclusion

Designers are a great way of adding that extra touch to any control, to make the developers life that much easier when configuring it at design time. Im aware Ive only scratched the surface in this article, but I intend to go much deeper in future tutorials. I hope Ive shown you enough to get you started writing your own designers. Consult the documentation for the other overridable members of the various designer classes, and dont be afraid to experiment!

View Full Article with Downloads
 
Back
Top