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

ComboBox List Control Host

0.00/5 (No votes)
9 Jan 2009 2  
An article demonstrating how to host list controls inside a standard ComboBox. Easily create your own CheckedList ComboBox and others.

CheckedComboBoxPic.JPG

Introduction

This article allows you to create your own fully customized list controls for display in a combobox drop down style while at the same time staying with as much intrinsic/standard control functionality as is possible. The drop window behaviour supports multiple select operations without closing the drop window, and does not use a single API call. The base combobox implementation uses the ToolStripDropDown component to replace the built in drop window functionality of the base combobox along with a ToolStripControlHost component used internally to host a third control. The base implementation allows a developer to extend functionality by implementing a variety of different controls. Ideally, only use list controls such as the following.

  • ListBox
  • CheckedListBox
  • MonthCalendar
  • TreeView
  • DataGridView

Background

The code for this article is the result of a request that came across my desk. My brief was to "give us a checked combobox like the one used in Team System". After some serious search engine abuse, I came up with nothing usable in any stable format that did exactly what I wanted. All the samples used API calls - badly at that too. I have a rule, when you need to use APIs calls... don't! So, I did the next best thing, and re-invented the wheel, which also led to my very first article which I've wanted to do for sometime now. Hats off to the prolific article writers out there - where you find the time, I couldn't even begin to fathom. Anyway, here goes.

Using the Code

Using the code is pretty straightforward. Within a Windows Forms application, we have Form1. The controls/base folders contain a base combobox implementation and a CheckedListComboBox implementation built on top of the base combobox implementation. Simply extract the source code to a folder near you, open it and build the project in VS 2008. Select the Items property to create a list and hit the Play button. For this version only, our control won't support data binding simply because the standard Windows CheckedListBox control doesn't support data binding - I will address this functionality in my next article.

Base Implementation

Choice of Container

Firstly, we must hide the base drop window size to appear invisible, then prepare our internal containers that are going to provide the necessary functionality, specifically:

  • 01 x standard ToolStripDropDown component, which is a marvelous little component that provides a host of functionality as ‘standard’. One particularly useful tasty morsel is that the behavior allows mouse click functionality to select listed items while at the same time maintaining an open drop list for subsequent select operations. Something that can only be properly achieved in C++. The sample code/articles I found all used API calls – which, besides being unstable, is an attempt to access the root C++ functionality anyway.
  • 01 x standard ToolStripControlHost, almost a Swiss army knife when it comes to its flexibility in wrapping any other control, and ease of use with almost no code required.
namespace CheckedComboTest.Controls.Base
{
    public abstract class ComboBoxEx : ComboBox
    {

        #region Declarations & Constructors

        private ToolStripDropDown tsdd = new ToolStripDropDown();
        private ToolStripControlHost tsch = null;

        private int dropDownHeight;

        new public event EventHandler DropDown;
        new public event EventHandler DropDownClosed;

        public ComboBoxEx()
            : base()
        {
            base.DropDownHeight = 1;
        }

        #endregion

Instantiation

Always important in any component design is the manner and timing of any internal container instantiation. That’s why I like to use the base OnHandleCreated method to build the internals because by the time the thread executes my code, I know the base control has been instantiated and my control instance has a handle attached to it.

#region Base Overrides/Overloads

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    //  our handle has now been officially created; build internals
    BuildInternalHost();
}

#endregion

Internally, we configure our drop container and call the abstract OnCreateHost() method, forcing any implementation up the hierarchy to provide a control for hosting at the same time as the creation of the host container is taking place.

//  call extended implementations to provide a guest control
tsch = new ToolStripControlHost(OnCreateHost());

All we need to do is provide our own drop height functionality, and we can proceed with a custom implementation of ComboBoxEx.

Custom Implementation

Our custom implementation needs a class that inherits from our ComboBoxEx base component, and for our example, let’s use a CheckedListBox control.

public class CheckedListComboBox : Base.ComboBoxEx
{

    #region Declarations & Constructors

    private CheckedListBox checkedListBox = new CheckedListBox();

    public CheckedListComboBox()
        : base()
    {
    }

    #endregion

Actually, all our implementation needs to do is return an instance of a list control back through the OnCreateHost() function, like this…

#region Base Overrides/Overloads

protected override Control OnCreateHost()
{
    return checkedListBox;
}

…and then we can paste from the toolbox onto the form and run the project. The resulting drop window is quite normal at first site, especially when compared with the standard ComboBox, till we actually click to display the drop windows. Our custom drop window has a drop shadow effect along with matching border colors, and all this as provided standard by Microsoft.

Lastly, replace the standard Items property with the one exposed by the CheckedListBox control et voila:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor,
    System.Design, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[Localizable(true)]
new public CheckedListBox.ObjectCollection Items
{
    get
    {
        return checkedListBox.Items;
    }
}

Points of Interest

Effectively, the only thing created was an intermediate layer of code allowing multiple components to be plugged into each other. We didn’t even have to ‘hack’ any drawing routine or use even a single API call. The available controls are simple building blocks, and while it is great fun to twist and 'hack' something to death, it is easy to overdo, especially when done without a clear idea of how to extend the framework.

In my next article, I will expand on my custom implementations to cover data binding on custom implementations.

History

First release!

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