Creating the Square control
Creating a custom control in windows forms is very simple – there are a few gotchas along the way – so I thought I’d make a sample control called “Square” to walk through it.
The square will obviously have sizing restrictions, but will also do some simple custom drawing.
Making sure we’re the right size
I want the size of my control to default to something, well, square shaped when dropped onto the form.
Therefore I want to control the initial size to be equal in width and height.
There are a couple of ways to do this.
In the constructor, I could say
public class Square : Control {
public Square() {
this.Size = new Size(100,100);
}
}
However when I expand InitializeComponent, I see the size written out at 100,100.
While this is correct, in general we try not to write things that are already the default value.
Most controls achieve this by overriding the DefaultSize property.
The magic of the DefaultSize property is executed in the constructor of the base class Control – that is – it sets up the default by saying:
public Control () {
this.Size = this.DefaultSize;
}
MSDN
suggests that for perf reasons, overriding is the best way to handle setting up the default. It’s also there for the designer. If the Size matches the DefaultSize, the default implementation of ShouldSerializeSize returns false – thus preventing it from being written to IntializeComponent
More about ShouldSerialize method
here
.
Changing around our code:
public class Square : Control {
public Square() {
}
protected override Size DefaultSize {
get { return new Size(100,100); }
}
}
….creates a 100 by 100 square when dropped onto the design surface.
Next time:
We'll try adding a splash of color by overriding OnPaint.
Creating the Square control
Creating a custom control in windows forms is very simple – there are a few gotchas along the way – so I thought I’d make a sample control called “Square” to walk through it.
The square will obviously have sizing restrictions, but will also do some simple custom drawing.
Making sure we’re the right size
I want the size of my control to default to something, well, square shaped when dropped onto the form.
Therefore I want to control the initial size to be equal in width and height.
There are a couple of ways to do this.
In the constructor, I could say
public class Square : Control {
public Square() {
this.Size = new Size(100,100);
}
}
However when I expand InitializeComponent, I see the size written out at 100,100.
While this is correct, in general we try not to write things that are already the default value.
Most controls achieve this by overriding the DefaultSize property.
The magic of the DefaultSize property is executed in the constructor of the base class Control – that is – it sets up the default by saying:
public Control () {
this.Size = this.DefaultSize;
}
MSDN
suggests that for perf reasons, overriding is the best way to handle setting up the default. It’s also there for the designer. If the Size matches the DefaultSize, the default implementation of ShouldSerializeSize returns false – thus preventing it from being written to IntializeComponent
More about ShouldSerialize method
here
.
Changing around our code:
public class Square : Control {
public Square() {
}
protected override Size DefaultSize {
get { return new Size(100,100); }
}
}
….creates a 100 by 100 square when dropped onto the design surface.
Next time:
We'll try adding a splash of color by overriding OnPaint.
One of the harder things to decide when you're starting out building a custom control is what to derive from. There are a couple different layers to the control hierarchy, and it's not always clear what to derive from.
|
Base class
|
What it adds
|
When to inherit from it
|
|
Component
|
The IDisposable pattern on top of System.Object as well as the ability to be designed using the stuff in
System.Design.
|
When you have an object with resources that you want to have the ability to be present on the design surface in some way.
|
|
Control
|
Mouse, Painting, Keyboard handling
|
When you want Mouse, Painting, Keyboard handling, and you do not expect to host child controls in a public API.
|
|
ScrollableControl
(inherits from Control)
|
Scrolling capabilities
|
As the docs suggest - you should not inherit from this class directly.
If you want the control itself to be able to take focus, then inherit from Panel, and set
ControlStyles.Selectable
to true.
If you only want child controls to take focus, inherit from ContainerControl.
|
|
ContainerControl
(inherits from ScrollableControl)
|
Advanced focus management.
|
A
ContainerControl
represents a control that can function as a container for other controls and provides focus management. Controls that inherit from this class can track the active control they contain, even when the focus moves somewhere within a different container.
ContainerControl
objects provide a logical boundary for contained controls. The container control can capture the TAB key press and move focus to the next control in the collection
Note
The container control does not receive focus; the focus is always set to the first child control in the collection of contained controls.
|
|
UserControl
(Inherits from ContainerControl)
|
Adds Load Event and Friendly design surface
|
If you have a set of controls you wish to reuse at several different places, this class is handy as it has full designer support for just loading up that panel of items and designing it separately.
|
|
Form
(Inherits from ContainerControl)
|
Adds Load Event, friendly design surface, and the ability to be a toplevel and/or topmost window.
|
Good for the main window of an application, MDI child windows, modal dialog windows, etc.
|
For reference, here's a picture of
full class hierarchy from V1
.