Click here to Skip to main content
11,934,389 members (54,654 online)
Click here to Skip to main content
Add your own
alternative version


105 bookmarked

How to create a custom ComboBox from scratch

, 13 Nov 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
An article on creating a custom ComboBox control completely from scratch.


A few weeks ago, I spent lots of time searching the web for a fully customized ComboBox which I could use in an application of mine. I didn't find any good looking free ones. I don't pretend that there is no such control created, but my search fault inspired me to build one myself. The following code example I have included is not exactly what I have in my application, but is a good point to introduce the way a custom combobox can be created.

How it works?

If we open MSDN and search a little, we will find that the .NET ComboBox extends the ListControl class. Basically, the ComboBox consists of a TextBox and a ListBox which appears on the screen as a popup window.

So what I did was just implement the ListControl class and add a textbox and a listbox equipped with the appropriate popup controls, in .NET 2.0.

Here is the class schema:


There are certain methods and properties that need to be overridden, overloaded, or dismissed in order to achieve the proper function for the combobox. I don't know from where to start, but it will be better to show the basic chapters of my work, and the rest can be seen in the attached source code.

Let's start with the constructor:

#region Constructor
public BNComboBox()
    //preperaing the basic control behavior
    SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    SetStyle(ControlStyles.ContainerControl, true);
    SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    SetStyle(ControlStyles.ResizeRedraw, true);
    SetStyle(ControlStyles.Selectable, true);
    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    SetStyle(ControlStyles.UserMouse, true);
    SetStyle(ControlStyles.UserPaint, true);
    SetStyle(ControlStyles.Selectable, true);

    //setting some variables
    base.BackColor = Color.Transparent;
    _radius.BottomLeft = 2;
    _radius.BottomRight = 2;
    _radius.TopLeft = 2;
    _radius.TopRight = 6;

    this.Height = 21;
    this.Width = 95;

    //adjusting the component controls
    _textBox = new TextBox();
    _textBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
    _textBox.Location = new System.Drawing.Point(3, 4);
    _textBox.Size = new System.Drawing.Size(60, 13);
    _textBox.TabIndex = 0;
    _textBox.WordWrap = false;
    _textBox.Margin = new Padding(0);
    _textBox.Padding = new Padding(0);
    _textBox.TextAlign = HorizontalAlignment.Left;

    //very important function that aligns the nested controls

    //adjusting the component controls
    _listBox = new ListBox();
    _listBox.IntegralHeight = true;
    _listBox.BorderStyle = BorderStyle.FixedSingle;
    _listBox.SelectionMode = SelectionMode.One;
    _listBox.BindingContext = new BindingContext();

    _controlHost = new ToolStripControlHost(_listBox);
    _controlHost.Padding = new Padding(0);
    _controlHost.Margin = new Padding(0);
    _controlHost.AutoSize = false;

    _popupControl = new ToolStripDropDown();
    _popupControl.Padding = new Padding(0);
    _popupControl.Margin = new Padding(0);
    _popupControl.AutoSize = true;
    _popupControl.DropShadowEnabled = false;

    _dropDownWidth = this.Width;

    //exposing the listbox event handlers 
    //to the outer control - the combobox
    _listBox.MeasureItem += 
        new MeasureItemEventHandler(_listBox_MeasureItem);
    _listBox.DrawItem += new DrawItemEventHandler(_listBox_DrawItem);
    _listBox.MouseClick += new MouseEventHandler(_listBox_MouseClick);
    _listBox.MouseMove += new MouseEventHandler(_listBox_MouseMove);

    _popupControl.Closed += 
        new ToolStripDropDownClosedEventHandler(_popupControl_Closed);

    _textBox.Resize += new EventHandler(_textBox_Resize);
    _textBox.TextChanged += new EventHandler(_textBox_TextChanged);


You can check the controls aligning function in the source.

In order to catch certain events on the combobox, I declared the following event handlers and delegates:

public delegate void BNDroppedDownEventHandler
    (object sender, EventArgs e);
public delegate void BNDrawItemEventHandler
    (object sender, DrawItemEventArgs e);
public delegate void BNMeasureItemEventHandler
    (object sender, MeasureItemEventArgs e);
#region Delegates

    Description("Occurs when IsDroppedDown changes to True.")]
public event BNDroppedDownEventHandler DroppedDown;

    Description("Occurs when the SelectedIndex property changes.")]
public event EventHandler SelectedIndexChanged;

    Description("Occurs when an item/area needs to be painted.")]
public event BNDrawItemEventHandler DrawItem;

    Description("Occurs when an item's height needs to be calculated.")]
public event BNMeasureItemEventHandler MeasureItem;


This is how I call the DrawItem event, for example:

void _listBox_DrawItem(object sender, DrawItemEventArgs e)
    if (e.Index >= 0)
        if (DrawItem != null)
            DrawItem(this, e);

Painting the control

Except changing the style of the control in the constructor, there are a number of other properties and methods to develop.

public new Color BackColor
    get { return _backColor; }
        this._backColor = value;
        _textBox.BackColor = value;

As you saw in the constructor, we set the BackColor property to Transparent, and we don't touch it any more. Instead, I use a local variable, and overload the base.BackColor, which in our case is ListControl.BackColor.

I also included four color properties and a Radius variable which can be used in the painting code to achieve a nice look and feel. Next, we have to add some mouse functionality like: handling the mouse-up, mouse-down, wheel, enter, leave, etc. Thus, the combobox can change its view when receiving focus, mouse hover, or click.

Finally, just paint the portions of the combobox:

protected override void OnPaint(PaintEventArgs e)
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    //content border
    Rectangle rectCont = rectContent;
    rectCont.X += 1;
    rectCont.Y += 1;
    rectCont.Width -= 3;
    rectCont.Height -= 3;
    GraphicsPath pathContentBorder = 
        CreateRoundRectangle(rectCont, Radius.TopLeft, Radius.TopRight, 
            Radius.BottomRight, Radius.BottomLeft);

    //button border
    Rectangle rectButton = rectBtn;
    rectButton.X += 1;
    rectButton.Y += 1;
    rectButton.Width -= 3;
    rectButton.Height -= 3;
    GraphicsPath pathBtnBorder = 
        CreateRoundRectangle(rectButton, 0, 
            Radius.TopRight, Radius.BottomRight, 0);

    //outer border
    Rectangle rectOuter = rectContent;
    rectOuter.Width -= 1;
    rectOuter.Height -= 1;
    GraphicsPath pathOuterBorder = 
        CreateRoundRectangle(rectOuter, Radius.TopLeft, 
            Radius.TopRight, Radius.BottomRight,

    //inner border
    Rectangle rectInner = rectContent;
    rectInner.X += 1;
    rectInner.Y += 1;
    rectInner.Width -= 3;
    rectInner.Height -= 3;
    GraphicsPath pathInnerBorder = 
        CreateRoundRectangle(rectInner, Radius.TopLeft, 
            Radius.TopRight, Radius.BottomRight,

    //brushes and pens
    Brush brInnerBrush = new LinearGradientBrush(
        new Rectangle(rectInner.X,rectInner.Y,rectInner.Width,
        (hovered || IsDroppedDown || ContainsFocus)?Color4:Color2, 
    Brush brBackground;
    if (this.DropDownStyle == ComboBoxStyle.DropDownList)
        brBackground = new LinearGradientBrush(pathInnerBorder.GetBounds(), 
            Color.FromArgb(IsDroppedDown ? 100 : 255, Color.White), 
            Color.FromArgb(IsDroppedDown?255:100, BackColor),
        brBackground = new SolidBrush(BackColor);
    Pen penOuterBorder = new Pen(Color1, 0);
    Pen penInnerBorder = new Pen(brInnerBrush, 0);
    LinearGradientBrush brButtonLeft = 
        new LinearGradientBrush(rectBtn, Color1, Color2, 
    ColorBlend blend = new ColorBlend();
    blend.Colors = new Color[] 
        { Color.Transparent, Color2, Color.Transparent };
    blend.Positions = new float[] { 0.0f, 0.5f, 1.0f};
    brButtonLeft.InterpolationColors = blend;
    Pen penLeftButton = new Pen(brButtonLeft, 0);
    Brush brButton = 
        new LinearGradientBrush(pathBtnBorder.GetBounds(),
        Color.FromArgb(100, IsDroppedDown? Color2:Color.White),
            Color.FromArgb(100, IsDroppedDown ? Color.White : Color2),

    e.Graphics.FillPath(brBackground, pathContentBorder);
    if (DropDownStyle != ComboBoxStyle.DropDownList)
        e.Graphics.FillPath(brButton, pathBtnBorder);
    e.Graphics.DrawPath(penOuterBorder, pathOuterBorder);
    e.Graphics.DrawPath(penInnerBorder, pathInnerBorder);

    e.Graphics.DrawLine(penLeftButton, rectBtn.Left + 1, 
        rectInner.Top+1, rectBtn.Left + 1, rectInner.Bottom-1);

    Rectangle rectGlimph = rectButton;
    rectButton.Width -= 4;
    e.Graphics.TranslateTransform(rectGlimph.Left + 
        rectGlimph.Width / 2.0f, 
        rectGlimph.Top + rectGlimph.Height / 2.0f);
    GraphicsPath path = new GraphicsPath();
    PointF[] points = new PointF[3];
    points[0] = new PointF(-6 / 2.0f, -3 / 2.0f);
    points[1] = new PointF(6 / 2.0f, -3 / 2.0f);
    points[2] = new PointF(0, 6 / 2.0f);
    path.AddLine(points[0], points[1]);
    path.AddLine(points[1], points[2]);

    SolidBrush br = new SolidBrush(Enabled?Color.Gray:Color.Gainsboro);
    e.Graphics.FillPath(br, path);

    if (DropDownStyle == ComboBoxStyle.DropDownList)
        StringFormat sf  = new StringFormat(StringFormatFlags.NoWrap);
        sf.Alignment = StringAlignment.Near;

        Rectangle rectText = _textBox.Bounds;
        rectText.Offset(-3, 0);

        SolidBrush foreBrush = new SolidBrush(ForeColor);
        if (Enabled)
            e.Graphics.DrawString(_textBox.Text, this.Font, 
                foreBrush, rectText.Location);
            ControlPaint.DrawStringDisabled(e.Graphics, _textBox.Text, 
                Font, BackColor, rectText, sf);
    Dim foreBrush As SolidBrush = New SolidBrush(color)
    If (enabled) Then
        g.DrawString(text, font, foreBrush, rect, sf)
        ControlPaint.DrawStringDisabled(g, text, font, backColor, _
             rect, sf)
    End If




Drop down

Another important thing is the drop down functions. The basic property which controls the ListBox popup is IsDroppedDown.

public bool IsDroppedDown
    get { return _isDroppedDown; }
        if (_isDroppedDown == true && value == false )
            if (_popupControl.IsDropDown)

        _isDroppedDown = value;

        if (_isDroppedDown)
            _controlHost.Control.Width = _dropDownWidth;


            if (_listBox.Items.Count > 0) 
                int h = 0;
                int i = 0;
                int maxItemHeight = 0;
                int highestItemHeight = 0;
                foreach(object item in _listBox.Items)
                    int itHeight = _listBox.GetItemHeight(i);
                    if (highestItemHeight < itHeight) 
                        highestItemHeight = itHeight;
                    h = h + itHeight;
                    if (i <= (_maxDropDownItems - 1)) 
                        maxItemHeight = h;
                    i = i + 1;

                if (maxItemHeight > _dropDownHeight)
                    _listBox.Height = _dropDownHeight + 3;
                    if (maxItemHeight > highestItemHeight )
                        _listBox.Height = maxItemHeight + 3;
                        _listBox.Height = highestItemHeight + 3;
                _listBox.Height = 15;

            _popupControl.Show(this, CalculateDropPosition(), 

        if (_isDroppedDown)
            OnDroppedDown(this, EventArgs.Empty);

There are still other properties and methods that need to be overridden. They can be seen in the attached code.

Using the code

Using the code is as simple as using the basic ComboBox control. There are some properties that are not implemented like in the original ComboBox control, but I leave it for future development.


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


About the Author

Software Developer
Bulgaria Bulgaria
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
Kanasz Robert27-Sep-12 11:58
mvpKanasz Robert27-Sep-12 11:58 
Questionclose droppedDown Pin
alireza_shamayeli24-May-12 10:09
memberalireza_shamayeli24-May-12 10:09 
QuestionpopupControl Problem Pin
Member 390465329-Sep-09 1:44
memberMember 390465329-Sep-09 1:44 
GeneralImprovement Pin
bbbnova14-Nov-08 22:42
memberbbbnova14-Nov-08 22:42 
GeneralGood work, but drop-down listbox needs more attention. Pin
Glynn14-Nov-08 5:55
memberGlynn14-Nov-08 5:55 
GeneralScroll bar not customized Pin
_Ratish_Philip_14-Nov-08 4:50
member_Ratish_Philip_14-Nov-08 4:50 

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.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.151126.1 | Last Updated 13 Nov 2008
Article Copyright 2008 by bbbnova
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid