Click here to Skip to main content
15,867,834 members
Articles / Programming Languages / C#
Article

Custom ComboBoxes with Advanced Drop-down Features

Rate me:
Please Sign up or sign in to vote.
4.95/5 (55 votes)
22 Apr 2003CPOL4 min read 312.7K   12K   125   60
Contains several ComboBoxes which uses Windows themes and contains ComboBoxes with CheckBoxed lists and TreeViews

Preview of cutom comboboxes

CheckComboBox control
DateComboBox control

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:

C#
// pseudo code
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.

C#
// pseudo code
protected virtual void On<PropertyName>Changed()
{
   // ... business logic of control ...

   Raise<PropertyName>ChangedEvent();
}

// event raiser
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:

C#
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:

C#
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.

C#
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.

C#
protected override void OnDropDownControlBinding( CustomCombo.EventArgsBindDropDownControl e )
{
  e.BindedControl = m_tree; // m_tree is a TreeView control
  m_tree.ImageList = m_imgList;
  RaiseFillTreeByData( e );

  // in case when we do data load on scroll message then
  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.

C#
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).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO ArtfulBits Inc.
Ukraine Ukraine
Name:Kucherenko Oleksandr

Born:September 20, 1979

Platforms: Win32, Linux; - well known and MS-DOS; Win16; OS/2 - old time not touched;

Hardware: IBM PC

Programming Languages: Assembler (for Intel 80386); Borland C/C++; Borland Pascal; Object Pascal; Borland C++Builder; Delphi; Perl; Java; Visual C++; Visual J++; UML; XML/XSL; C#; VB.NET; T-SQL; PL/SQL; and etc.

Development Environments: MS Visual Studio 2001-2008; MS Visual C++; Borland Delphi; Borland C++Builder; C/C++ any; Rational Rose; GDPro; Together and etc.

Libraries: STL, ATL, WTL, MFC, NuMega Driver Works, VCL; .NET 1.0, 1.1, 2.0, 3.5; and etc.

Technologies: Client/Server; COM; DirectX; DirectX Media; BDE; HTML/DHTML; ActiveX; Java Servlets; DCOM; COM+; ADO; CORBA; .NET; Windows Forms; GDI/GDI+; and etc.

Application Skills: Databases - design and maintain, support, programming; GUI Design; System Programming, Security; Business Software Development. Win/Web Services development and etc.

Comments and Discussions

 
AnswerRe: In the web ? Pin
Oleksandr Kucherenko24-Dec-03 22:18
Oleksandr Kucherenko24-Dec-03 22:18 
QuestionHow to show in NON-XP look and feel? Pin
Member 59970310-Dec-03 21:58
Member 59970310-Dec-03 21:58 
AnswerRe: How to show in NON-XP look and feel? Pin
Oleksandr Kucherenko10-Dec-03 22:51
Oleksandr Kucherenko10-Dec-03 22:51 
GeneralRe: How to show in NON-XP look and feel? Pin
yianfang3-Jul-04 6:37
yianfang3-Jul-04 6:37 
GeneralRe: How to show in NON-XP look and feel? Pin
Oleksandr Kucherenko4-Jul-04 20:40
Oleksandr Kucherenko4-Jul-04 20:40 
GeneralRe: How to show in NON-XP look and feel? Pin
Oleksandr Kucherenko4-Jul-04 22:18
Oleksandr Kucherenko4-Jul-04 22:18 
GeneralRe: How to show in NON-XP look and feel? Pin
moon4429-Apr-05 7:56
moon4429-Apr-05 7:56 
GeneralDatabinding to Datatable Pin
ubeck@1eeurope.ch6-Nov-03 4:35
ubeck@1eeurope.ch6-Nov-03 4:35 
Hi
I ran into some problems trying to bind the treecombo to a datatable
I want my Treecombo to show Company-Department Text Infos with its Ids stored in the Tag Property. The Ids should be bound to the DataTables DepartmentID Column.
The Combotext is empty after Form Load.
How can I Bind the TreeCombo to my DataTable ?

TreeCombo:
Company A- Dept1 (=Text Property)
-Dept2
-Dept3
Company B -Dept1
-Dept5
-Dept3
etc..



Bindings:
...
txtPhone.DataBindings.Add("Text", Dataset, "UserTable.Phone")
txtEmail.DataBindings.Add("Text", mDs, "UserTable.EMail")
TreeComboCompanyDept.TreeDropDown.DataBindings.Add("Tag", Dataset, "UserTable.DepartmentID")
...

Fill TreeNodes in Combo:
...
Dim tn As New TreeNode()
tn.Text = DisplayText
tn.Tag = Id
e.BindedControl.Nodes.Add(tn)
...

Thanks

GeneralRe: Databinding to Datatable Pin
Oleksandr Kucherenko6-Nov-03 4:49
Oleksandr Kucherenko6-Nov-03 4:49 
GeneralGot Focus Pin
klubell23-Oct-03 11:02
klubell23-Oct-03 11:02 
GeneralRe: Got Focus Pin
klubell27-Oct-03 3:21
klubell27-Oct-03 3:21 
GeneralPainting the control elements Pin
DaveHStone3-Sep-03 2:37
DaveHStone3-Sep-03 2:37 
GeneralRe: Painting the control elements Pin
Oleksandr Kucherenko3-Sep-03 2:44
Oleksandr Kucherenko3-Sep-03 2:44 
GeneralBest way to implement on key down Pin
Gmonkey13-Aug-03 7:27
Gmonkey13-Aug-03 7:27 
GeneralRe: Best way to implement on key down Pin
Oleksandr Kucherenko24-Aug-03 21:10
Oleksandr Kucherenko24-Aug-03 21:10 
GeneralDatabinding Pin
warrenroscoe11-Jul-03 0:06
warrenroscoe11-Jul-03 0:06 
GeneralRe: Databinding Pin
Oleksandr Kucherenko11-Jul-03 0:13
Oleksandr Kucherenko11-Jul-03 0:13 
GeneralOnDropDownControlBinding Pin
warrenroscoe9-Jul-03 4:02
warrenroscoe9-Jul-03 4:02 
GeneralRe: OnDropDownControlBinding Pin
Oleksandr Kucherenko9-Jul-03 4:16
Oleksandr Kucherenko9-Jul-03 4:16 
GeneralRe: OnDropDownControlBinding Pin
warrenroscoe9-Jul-03 4:49
warrenroscoe9-Jul-03 4:49 
GeneralCheckItem, UnCheckItem, IsItemChecked Pin
warrenroscoe3-Jul-03 2:04
warrenroscoe3-Jul-03 2:04 
GeneralRe: CheckItem, UnCheckItem, IsItemChecked Pin
Oleksandr Kucherenko3-Jul-03 2:18
Oleksandr Kucherenko3-Jul-03 2:18 
GeneralGood controls. Pin
Rocky Moore23-Apr-03 5:53
Rocky Moore23-Apr-03 5:53 
GeneralRe: Good controls. Pin
John O'Byrne23-Apr-03 22:35
John O'Byrne23-Apr-03 22:35 
GeneralRe: Good controls. Pin
Oleksandr Kucherenko25-Apr-03 22:58
Oleksandr Kucherenko25-Apr-03 22:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.