Preview of cutom comboboxes
Introduction
During development of Windows Forms application I found that .NET controls do
not satisfy me. After two days and many bug fixes I made satisfactory controls.
I think this can help someone else. My controls sometimes use UtilityLibrary
written by Carlos
H. Perez. Thanks to him for good job ( sometimes some bugs are found, but
this library give to me a good example how to create my own controls).
Code Bases
For a base control I use the System.Windows.Forms.Control
class.
For a child control I use the TextBox
control. It is used by Combo
only in cases when user sets the control in edit mode. The code structure is
simple enougth and is used by me for all controls. All properties are made by
the same template:
public datatype PropertyName
{
get
{
return internalVariable;
}
set
{
if( value != internalVariable )
{
internalVariable = value;
On<PropertyName>Changed();
}
}
}
As you can see, properties always have in internal variables which are used
by me sometimes as a cache for user data. The main thing in property
implementation of the control is that I always check if it is a new value, or
rather different from old one. In many cases this can help us to stop event
raising recursion.
Second thing in implementation is that after setting the
new value I always call On<PropertyName>Changed();
. In such
methods I always implement logic of control and check how control must
invalidate itself.
protected virtual void On<PropertyName>Changed()
{
Raise<PropertyName>ChangedEvent();
}
private void Raise<PropertyName>ChangedEvent()
{
if( <PropertyName>Changed != null )
{
<PropertyName>Changed( this, EventArgs.Empty );
}
}
Why do I always raise events when the value of property changed? After
reading on the .NET 247 site many articles about databinding features of .NET
controls, I made a decision to always implement Property Change events. They
help a lot when you try to use databinding in application and give developers
good flexibility in the implementation of any control's logic. Databinding them
can take too much time and will not be discussed in this article.
As you can see my controls always use such templatse to implement property
logic.
Code
All combos are inherited from the base class CustomCombo
which
is implemented in the UtilityLibrary\Combos\CustomComboBox.cs file. The
base class is abstract and all inheritors must implement such methods:
protected abstract void OnPrevScrollItems();
protected abstract void OnNextScrollItems();
protected abstract void OnDropDownControlBinding( EventArgsBindDropDownControl e );
protected abstract void OnValueChanged();
Description:
OnPrevScrollItems
- say control to change
value of combo to previous.
OnNextScrollItems
- say control to
change value of combo to next one.
OnDropDownControlBinding
-
Special method which used by inheritors to attach own dropdown control to combo.
OnValueChanged()
- method used to check an correct value of combo.
This method called by Value property code.
Also control has some additional methods which can be overrided by
inheritors:
protected virtual bool OnValueValidate( string value );
protected virtual void OnDropDownSizeChanged();
protected virtual void OnDropDownFormLocation();
Description:
OnValueValidate
- method make validation
of incoming values.
OnDropDownSizeChanged
- method calculate
size of dropdown form.
OnDropDownFormLocation
- method calculate
where dropdown form must be shown.
Also the abstract class has some helper methods which can be used by the user
to boost combo fill.
public void BeginUpdate();
public void EndUpdate();
Description:
BeginUpdate
- control skip all
invalidation code in class
EndUpdate
- control start to
invalidate itself after changes
How to Implement Your Own ComboBox
First step
Combobox is designed to support two modes: readonly and editable. In readonly
mode the control does its own drawing of data and in editable mode all data
drawing makes an internal TextBox control. The state of the control can be
controled by the Readonly
property.
So first of all select which type of combo you want to implement. I always
try to implement both states of control.
Second step
The second step of implementatin is to select which control must be used in
the drop down form. For that purposes class use
OnDropDownControlBinding
abstract method.
protected override void OnDropDownControlBinding( CustomCombo.EventArgsBindDropDownControl e )
{
e.BindedControl = m_tree;
m_tree.ImageList = m_imgList;
RaiseFillTreeByData( e );
m_ctrlBinded = m_tree;
m_bControlBinded = true;
}
Third step
The third step of implementation is optional and can be skipped. It's needed
only when you want to make your own custom drawing of the combo value.
protected virtual void OnItemSizeCalculate( object sender, CustomCombo.EventArgsEditCustomSize e )
{
if( m_imgList != null )
{
int iWidth = m_imgList.ImageSize.Width + 2;
e.xPos += iWidth;
e.Width -= iWidth;
}
}
protected override void OnPaintCustomData(System.Windows.Forms.PaintEventArgs pevent)
{
Graphics g = pevent.Graphics;
Rectangle rc = pevent.ClipRectangle;
if( m_tree.SelectedNode != null && m_imgList != null )
{
Rectangle rcOut = new Rectangle( rc.X + 2, rc.Y+2, m_imgList.ImageSize.Width, rc.Height - 4 );
int index = m_tree.SelectedNode.ImageIndex;
if( m_imgList.Images.Count > index && m_imgList.Images.Count > 0 )
{
if( index < 0 ) index = 0;
Image img = m_imgList.Images[ index ];
g.DrawImage( img, rcOut );
}
}
}
The first method calculates the area in which the developer wants to draw and
what does not. The second method is a paint method. CustomCombo
give you a chance to override and implement your own drawing for all items of
the control. The paint method can logically be split into background drawing
methods and item drawing. In most cases background drawing is no needed to
override, but you can do that.
Data flow
When a user types any text in combo then we first check is data old or new,
then we validate the value by calling the OnValueValidate
method,
and if it return true then call the OnValueChanged
method.
Some features
In my implementation of the controls I use following techniques: data loading
on user demand... This feature you can find in the TreeCombo
class
implementation.
Known Bugs or Not Yet Implemented Features
In Readonly mode not all keybord functions work. (Up/Down arrows do not
work).