(Print this page)

Preventing a combo box item from being selected
Published date: Sunday, June 18, 2017
On: Moer and Éric Moreau's web site

It is not my first article about .Net combo boxes. The control is really basic and developers need ways to have it fit their needs.

Lately on Experts-Exchange, somebody asked that question. I felt like I should share my answer to a broader audience!

You might wonder yourself why would one would like to show an item that the user wouldn’t be able to select? What if some options were available in the past and are no longer? You might want to show the original value that was saved but not let the user select it if he ever changes it.

This month’s downloadable code

This month solution contains both VB and C# projects. The solution was created using Visual Studio 2017 but the code should work as well in older version of the.Net Framework.

Figure 1: The demo application in action

Creating a ComboBoxItem class

I wanted to have a property on a combo box item to mark is as selectable or not. At first, I thought I would use the Tag property but combo box items don’t have this property.

So I created a small combo box item class with the very basic members:

Public Class ComboboxItem

    Public Text As String
    Public Value As Object
    Public Selectable As Boolean = True

    Public Overrides Function ToString() As String
        Return Text
    End Function

End Class
public class ComboboxItem
{

    public string Text;
    public object Value;
    public bool Selectable = true;

    public override string ToString()
    {
        return Text;
    }
}

There is nothing really special in that class. Simply 3 public properties and an override of the ToString method.

Filling the combo box

Instead of filling your combo with plain old item, we will use our own ComboBoxItem to add items to the combo. This will let us specify which items are selectable (default) or not.

Here I create dummy items and I have decided arbitrarily that item 4 wouldn’t be selectable. You will need to use your own logic to specify if each item is selectable or not:

Private Sub FillCombo()
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed

    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test1", .Value = 1})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test2", .Value = 2})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test3", .Value = 3})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test4", .Value = 4, .Selectable = False})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test5", .Value = 5})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test6", .Value = 6})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test7", .Value = 7})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test8", .Value = 8})

    'Test manual setting of an unselectable item
    mblnManualSet = True
    ComboBox1.SelectedIndex = 3
    mblnManualSet = False
End Sub
private void FillCombo()
{
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed;

    ComboBox1.Items.Add(new ComboboxItem { Text = "Test1", Value = 1 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test2", Value = 2 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test3", Value = 3 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test4", Value = 4, Selectable = false });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test5", Value = 5 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test6", Value = 6 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test7", Value = 7 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test8", Value = 8 });

    ComboBox1.DrawItem += ComboBox1_DrawItem;
    ComboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;

    //Test manual setting of an unselectable item
    _blnManualSet = true;
    ComboBox1.SelectedIndex = 3;
    _blnManualSet = false;
}

The next section will tell you why the DrawMode has been set.

Finally, I use a way to be able to manually set the selected item to one that shouldn’t by using a private variable that will be reused in the SelectedIndexChanged event.

This method is called when the form loads.

Drawing items

In the FillCombo method, we have set the DrawMode property to OwnerDrawFixed. That will trigger the DrawItem event when items are to be drawn.

That will let us draw non-selectable items differently (italic and strikeout in this demo).

This is the code to draw items depending on the state of the Selectable property:

Private Sub ComboBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ComboBox1.DrawItem
    Dim objItem As ComboboxItem = CType(ComboBox1.Items(e.Index), ComboboxItem)
    If Not objItem.Selectable Then
        'draw an unselectable item
        e.Graphics.DrawString(ComboBox1.Items(e.Index).ToString(), myFont2, Brushes.LightSlateGray, e.Bounds)
    Else
        'draw a regular item
        e.DrawBackground()
        e.Graphics.DrawString(ComboBox1.Items(e.Index).ToString(), myFont, Brushes.Black, e.Bounds)
        e.DrawFocusRectangle()
    End If
End Sub
private void ComboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    ComboboxItem objItem = (ComboboxItem)ComboBox1.Items[e.Index];
    if (!objItem.Selectable)
    {
        //draw an unselectable item
        e.Graphics.DrawString(ComboBox1.Items[e.Index].ToString(), _myFont2, Brushes.LightSlateGray, e.Bounds);
    }
    else
    {
        //draw a regular item
        e.DrawBackground();
        e.Graphics.DrawString(ComboBox1.Items[e.Index].ToString(), _myFont, Brushes.Black, e.Bounds);
        e.DrawFocusRectangle();
    }
}

Selecting an item

When an item is selected (manually by code or from a user action), the SelectedIndexChanged is triggered.

We use this event here to just ensure that a non-selectable item will ever be selected except when explicitly asked for:

Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
    'If manual set it true
    If mblnManualSet Then Return

    'prevent setting an unselectable item
    Dim objItem As ComboboxItem = CType(ComboBox1.SelectedItem, ComboboxItem)
    If objItem Is Nothing Then Return
    If Not objItem.Selectable Then ComboBox1.SelectedIndex = -1
End Sub
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    //If manual set it true
    if (_blnManualSet) return;

    //prevent setting an unselectable item
    ComboboxItem objItem = (ComboboxItem)ComboBox1.SelectedItem;
    if (objItem == null)
        return;
    if (!objItem.Selectable)
        ComboBox1.SelectedIndex = -1;
}

Conclusion

Many times, we find ourselves in a situation where a control does not behave exactly like we want but a very little amount of code can do the job.


(Print this page)