Click here to Skip to main content
15,868,292 members
Articles / Programming Languages / C#

Custom List Box Control with Enable/Disable Items Feature

Rate me:
Please Sign up or sign in to vote.
4.44/5 (7 votes)
2 Jun 2010CPOL2 min read 53.6K   1.3K   22   2
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.

C#
private Netdev.Windows.Forms.ListBox listBox1; 
C#
// Create a listbox object.
this.listBox1 = new Netdev.Windows.Forms.ListBox(); 
C#
// Disable the item at the integer location index.
listBox1.DisableItem(index); 
C#
// Enable the item at the integer location index.
listBox1.EnableItem(index); 
C#
// 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.

C#
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:

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

C#
 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
I've been working with .Net since 2006, I use C# as my favorite programming language and I have a strong knowledge about .Net frameworks, Windows and Web development.

I'm interested in a broad range of technologies that include ASP.net, ADO.Net, WinForms, WPF, Silverlight as well as Best practices in development and design patterns.

I hold currently the following certifications:
MCTS: .NET Framework 2.0 Web Applications
MCTS: .NET Framework 2.0 Windows Applications
MCTS: .NET Framework 2.0 Distributed Applications
MCPD: Designing and Developing Enterprise Applications by Using the Microsoft .NET

For more posts:
C# and Windows Form Programming

Walkthrough on how to create a Control Extender in C#

Control Anchoring Class

Comments and Discussions

 
QuestionProblem with datatable Pin
RikyX P12-Nov-12 9:45
RikyX P12-Nov-12 9:45 
GeneralInteresting idea Pin
S.H.Bouwhuis7-Jun-10 21:54
S.H.Bouwhuis7-Jun-10 21:54 

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.