Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom ComboBoxes with Advanced Drop-down Features

0.00/5 (No votes)
22 Apr 2003 20  
Contains several ComboBoxes which uses Windows themes and contains ComboBoxes with CheckBoxed lists and TreeViews

Preview of cutom comboboxes

CheckComboBox control
DateComboBox control
TreeComboBox 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:

  // 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.

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

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

  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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here