Click here to Skip to main content
Click here to Skip to main content

Owner Drawn Controls - Extendable ListBox

, 15 Mar 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
This is the first in a series of articles on Owner Drawn controls, featuring the ListBox control. The basic fuctionality and some handy tips are included to help get you started in developing your own Owner Drawn controls.

Screenshot

Introduction

This is the first in a series of Owner Drawn control How-tos. The object is not to make a glitzy control with a bunch of bells and whistles, that is left to your imagination, but to demonstrate one way to create your own Owner Drawn ListBox control.

This control is different from the other derived ListBox controls that I've seen on CP in that this allows the items to be in either a collapsed or extended state. In other words, when you click on an item, it toggles the item view so that it can be minimized or maximized, allowing for additional information to be viewed when maximized. In the screenshot, the "Just Chill Man!" item is extended, all others are collapsed.

For this demo, I have created an ExtendedListBoxItem class, derived from an object that is used to maintain information about the item, and contains the methods DrawCollapsed, DrawExtended, DrawBorder, and AddGlow. This class is only a template to show what is possible and a beginning point for building your own item class. I'm sure that once these skills are mastered, you will be able to create a bigger, badder, better ListBoxItemControl class that will serve your particular needs.

Getting Started

The ExtendedListBoxControl is derived from the System.Windows.Forms.ListBox control, with the following property set:

ExtendedListBoxControl.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;

This is what allows us to draw the items ourselves. Setting the DrawMode to OwnerDrawVariable means that the items can be of any size that we want to, with a maximum of 255 pixels.

In addition, there are two virtual methods that we must override to enable us to actually do the drawing:

  • OnMeasureItem - Determines what size the item is going to be.
  • OnDrawItem - The method that actually draws the item.

One additional method that we will override is the:

  • OnMouseDown - Captures mouse down events.

These three methods are described in more detail in the next section.

The Overrides

As mentioned, there are two virtual methods we must override to be able to draw our own controls. The first of these is the OnMeasureItem method. This method is called to determine the size to draw the item. In our case, it depends on whether the item is in the collapsed or extended state. These values are determined by the MinSize (collapsed size) and MaxSize (extended size) properties.

/// 
/// Called when invalidated to find the item bounds. Since we are only concerned 
/// with the height we only need to set this value and leave the bounds.width 
/// as is! 
///
protected override void OnMeasureItem(System.Windows.Forms.MeasureItemEventArgs e) 
{
    base.OnMeasureItem(e); 

    if ((e.Index < 0) || (e.Index >= itemCache.Count))
        return; 

    ExtendedListBoxItem xlbi = (ExtendedListBoxItem) itemCache[e.Index];

    //If its the current selection and not collapsed 
    // set height to Max. otherwise just set the item selected to Min. value.
    if ((e.Index == _currentIndex) && !isCollapsed) 
        e.ItemHeight = xlbi.MaxSize; 
    else
        e.ItemHeight = xlbi.MinSize; 

    e.ItemWidth = this.Width; 
}

We finally get to the most important method, the one that actually does the drawing. This method uses the isCollapsed property to determine whether to draw the item in the collapsed or extended state.

///
/// Called when the item needs to be drawn
///    
protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
    base.OnDrawItem(e);

    
    //If not a valid index just ignore      
    if ((e.Index < 0) || (e.Index >= itemCache.Count))
        return;

    ExtendedListBoxItem xlbi = (ExtendedListBoxItem) itemCache[e.Index];

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    
    //Draw the item in current state.
    if ((e.Index == _currentIndex) && !isCollapsed)
        xlbi.DrawExpanded(e);
    else
        xlbi.DrawCollapsed(e);
}

The drawing of the item is delegated to the item itself; in this way, you can use the base class to provide basic functionality, roll your own or derive a class from ExtendedLstBoxItem, and use any or all of its functionality to suit your needs.

An index value is passed in via the DrawItemEventArgs. We use this index to retrieve a reference to the item, then call its appropriate DrawXXXX method to do the dirty work!

Next, we override the OnMouseDown instead of the OnSelectedIndexChanged, because the latter only gets called when the user clicks on a different index than the one selected, and we need to capture the mouse down event no matter which item is selected. I ran into a problem when trying to invalidate the list items, when selected. When trying to either expand or collapse the item, the OnMeasureItem handler wasn't getting called. To solve this, I Googled and ran across an obscure article that suggested that the item to be invalidated must be removed then reinserted into the list. The following code does this for the Previous and Current items:

protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) 
{
    base.OnMouseDown(e); 
    
        :
        :
    
    //Invalidate previous selection 
    InvalidateItem(_previousIndex); 
    
    //Invalidate current selection 
    InvalidateItem(_currentIndex); 
}

/// 
/// Invalidates the item at the provided index.
///
public void InvalidateItem(int index) 
{
    if ((index < 0) || (index >= itemCache.Count)) 
        return; 

    //All we need to do here is make sure
    //we get the correct item index.
    this.Items.RemoveAt(index); 
    this.Items.Insert(index, " "); 
}

Points of Interest

When running the demo, I have provided a PropertyGrid so that items can be designed visually. This feature is handy as it allows you to play with the different properties and see what the heck is going on without having to recompile every time you make a change. Thank you Microsoft for this handy control.

Note: The MinSize and MaxSize properties do not take effect immediately; you must toggle the item for changes to be reflected.

I hope this article has been of help!

License

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

Share

About the Author

Mike Hankey
Retired
United States United States
I'm an old assembler and C programmer self taught in the object oriented languages and Web design. Upon graduating from Murray State University in 1983 I Started doing embedded systems for the automotive industry, got a wild hair and moved to Florida in 1986 and did some work on DEC PDP and MicroVax computers.
My latest project:
ToDoManager extension for VS2010 and Atmel Studio 6.1

Comments and Discussions

 
QuestionDatabinding Pinmember4Bane6-Mar-14 2:25 
AnswerRe: Databinding PinmemberMike Hankey6-Mar-14 2:52 
GeneralRe: Databinding Pinmember4Bane6-Mar-14 2:58 
GeneralMy vote of 5 Pinmemberalleqs22-Jun-11 11:40 
QuestionHow can we disable Collapsing of items? Pinmembervarunchaddha5-Sep-10 6:33 
AnswerRe: How can we disable Collapsing of items? PinmemberMike Hankey6-Sep-10 6:02 
QuestionScreen Flicker Pinmemberdbalogh25-May-09 3:00 
AnswerRe: Screen Flicker PinmemberMike Hankey6-Sep-10 6:04 
GeneralThanks very much. Pinmemberpapillonhn15-Apr-07 18:22 
GeneralRe: Thanks very much. PinmemberMike Hankey16-Apr-07 2:04 
GeneralMIssing XLBCTest project PinmemberChrisBennet15-Mar-07 1:16 
GeneralRe: MIssing XLBCTest project PinmemberMike Hankey15-Mar-07 1:59 
GeneralNice job! Pinmemberthompsons8-Mar-07 15:46 
GeneralRe: Nice job! PinmemberMike Hankey8-Mar-07 16:07 
GeneralRe: Nice job! PinmemberSenthil Kumar U8-Mar-07 19:01 
GeneralRe: Nice job! PinmemberMike Hankey9-Mar-07 0:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web04 | 2.8.141015.1 | Last Updated 15 Mar 2007
Article Copyright 2007 by Mike Hankey
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid