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

Custom List Box Control with Enable/Disable Items Feature

0.00/5 (No votes)
2 Jun 2010 1  
C# custom Listbox control that has Enable/Disable items feature

Introduction

I worked once on a case where I had to show on a System.Windows.Forms.ListBox control some items that end user cannot select, but still they have to be shown anyway since the end user was expecting them. The point is that System.Windows.Forms.ListBox does not provide such a feature, so I decided to create my own user control and share it.

Control at Runtime

Here are some snapshots of the control at run time: [You can download the sample project build and run it].

Image1.JPG

after disabling Item3 [with Index 2] we will get:

Image2.JPG

Now Item3 cannot be selected, we will do the same with Item5 [Index4] and get:

Image3.JPG

Enabling Item3 [Index2] will revert the item at its default state:

Image4.JPG

Using the Code

Using this control is fairly straightforward. It is used in the same way the built-in ListBox control is used.

private Netdev.Windows.Forms.ListBox listBox1; 
// Create a listbox object.
this.listBox1 = new Netdev.Windows.Forms.ListBox(); 
// Disable the item at the integer location index.
listBox1.DisableItem(index); 
// Enable the item at the integer location index.
listBox1.EnableItem(index); 
// We can also wire an event handler for Disabled items selection event.
// Whenever an end user tries to select a disabled item this event will be fired
this.listBox1.DisabledItemSelected += new System.EventHandler
	<Netdev.Windows.Forms.IndexEventArgs>(this.listBox1_DisabledItemSelected);

Describing the Code

The core of the customization happens on the OnDrawItem method override. However the runtime will call this method only if DrawMode = DrawMode.OwnerDrawFixed; which is the default value of the control property at creation and a list of disabled Indices [items] as shown in this constructor.

public ListBox()
{
      DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
      disabledIndices = new DisabledIndexCollection(this);
}    

DisabledIndexCollection: This is a custom collection that is identical to the one holding SelectedIndices property. This collection tracks the indices that are currently disabled in the list which helps OnDrawItem method override to display those items as grayed items and prevent the end user from selecting them.

Here is the code of OnDrawItem method:

protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
    base.OnDrawItem(e);
    if (DesignMode  && Items.Count == 0)
    {
        if (e.Index == 0)
        {
            // Draw Control name as an item is design mode.
            e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
            e.Graphics.DrawString(this.Name, e.Font, SystemBrushes.WindowText, e.Bounds);
        }
        return;
    }

    if (e.Index != ListBox.NoMatches)
    {
        object item = this.Items[e.Index];
        // Get the text to display, this depends if the list is bound to data and 
        // DisplayMember, EnableFormating and FormatString are set.
        string displayValue = GetItemText(item);
        
        if (disabledIndices.Contains(e.Index))
        {
            e.Graphics.FillRectangle(SystemBrushes.InactiveBorder, e.Bounds);
            e.Graphics.DrawString(displayValue, e.Font, SystemBrushes.GrayText, e.Bounds);
        }
        else
        {
            if (SelectionMode == System.Windows.Forms.SelectionMode.None)
            {
                e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
                e.Graphics.DrawString(displayValue, e.Font, 
			SystemBrushes.WindowText, e.Bounds);
            }
            else
            {
                if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                {
                    e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
                    e.DrawFocusRectangle();
                    e.Graphics.DrawString(displayValue, e.Font, 
			SystemBrushes.HighlightText, e.Bounds);
                }
                else
                {
                    e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
                    e.Graphics.DrawString(displayValue, e.Font, 
			SystemBrushes.WindowText, e.Bounds);
                }
            }
        }
    }
}   

If an item is held in DisabledIndices collection, then display it as grayed.

And finally make sure to intercept SelectedIndexChanged event and swallow it if the selected item is grayed by overriding OnSelectedIndexChanged method.

 protected override void OnSelectedIndexChanged(EventArgs e)
{
    // Retrieve new value of selected index.
    int currentSelectedIndex = SelectedIndex;
    List<int> selectedDisabledIndices = new List<int>();

    for (int i = 0; i < SelectedIndices.Count; i++)
    {
        // unselect items that are already disabled.
        if (disabledIndices.Contains(SelectedIndices[i]))
        {
            selectedDisabledIndices.Add(SelectedIndices[i]);
            SelectedIndices.Remove(SelectedIndices[i]);
        }
    }
    foreach (int index in selectedDisabledIndices)
    {
        // Fire DisabledItemSelected event for each 
        // disabled item that has been selected.
        IndexEventArgs args = new IndexEventArgs(index);
        OnDisabledItemSelected(this, args);
    }
    // if updated selected index is different than the 
    // original one then bubble up the event
    if (currentSelectedIndex == SelectedIndex)
        base.OnSelectedIndexChanged(e);
}	 

History

  • Updated version - Fixed an issue related to the items display value when the list is bound to data, or DisplayMember, EnableFormating and FormatString are set

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