WPF: Making combo box items disabled – also when accessed using the keyboard

When I first embarked on WPF I ran into a number of small problems. Here is one of them.

The problem

I tried various ways to make my data bound combo box items disabled. I found that binding the ComboboxItem.IsEnabled to a property that indicates whether the item should be enabled did the trick.

But this did not work when I selected items using the keyboard. I also tried to make these not focusable but with the same result.

What worked: Click the combo down with the mouse and see that items that must be disabled can in fact not be selected.

What not worked: Use tab and arrow down to select items. They are not disabled and I can select them just fine with keyboard keys. Funny enough, if I click the combo down with the mouse, then the items are in fact disabled and cannot be selected with the keyboard keys.

Here is the XAML (I set up the data, meaning the properties FieldDomainValues, IsSelectable and ValueAsString, in constructers in code-behind):

<ComboBox x:Name="cmbx_test" ItemsSource="{Binding Path=FieldDomainValues}">
    <ComboBox.ItemContainerStyle>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding ="{Binding IsSelectable}" Value="False">
                    <Setter Property="ComboBoxItem.Focusable" Value="False"/>
                    <Setter Property="ComboBoxItem.IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ComboBox.ItemContainerStyle>
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=ValueAsString }"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

What did I do wrong here?

The workaround

Before we dig into the core reason for this problem, let’s have a look at a simple work-around that Parag Bhand suggested,

We also faced similar issues while dealing with binding in combobox. Once we open the dropdown every thing works fine (even from keyboard) then onwards. I guess it has something to do with ItemsContainerGenerator because item containers are not generated until dropdown is opened at least once.

In fact if we open the drop down and close it again programmatically in window’s loaded event handler then also it works fine.

cmbx_test.IsDropDownOpen = true;
cmbx_test.IsDropDownOpen = false;

This is what I do now and it works.

The explanation

Dwayne Need, found the explanation,

ComboBox.SelectItemHelper has logic to determine if the item and its corresponding container allow selection. When there is no container, this logic always returns True. The containers (ComboBoxItems) aren’t created until the dropdown box is measured, which doesn’t happen until the user causes the dropdown to appear, typically by clicking on its down-arrow button.

If you followed that, it’ll be clear why the workaround works. It causes a measure on the dropdown, which generates the containers. After that, SelectItemHelper’s logic picks up the state the app has declared for the containers. It should also be clear why we don’t do that for you automatically – the workaround creates UI that is never displayed.

This looks like a scenario bug to me. While the technical details make sense, the end-to-end scenario is broken.

Please file a bug.

This has now been filed as a bug (750993: Unable to disable combo-box items when selected with the keyboard).

Unfortunately the fix for this bug will not make it into .NET 4.0 so we must try and get around with the workaround for now.

4 thoughts on “WPF: Making combo box items disabled – also when accessed using the keyboard

  1. I was not able to open and close each ComboBox on form load. I came up with this work around and thought I’d share… Since in these scenarios you are likely binding to an object for IsEnabled I caught the CombBox.SelectionChangedEvent and did this:

    Public Sub OnComboBoxSelectionChanged(sender As Object, e As SelectionChangedEventArgs)
    If IsUILoaded AndAlso TypeOf (e.OriginalSource) Is ComboBox Then
    Dim cb As ComboBox = e.OriginalSource

    If e.AddedItems.Count > 0 AndAlso TypeOf (e.AddedItems(0)) Is RefCode Then
    ‘//check to be sure it is active / enabled
    If Not CType(e.AddedItems(0), RefCode).Active Then
    cb.SelectedItem = e.RemovedItems(0)
    End If
    End If
    End If
    End Sub

    Maybe that will help someone. What an irritating bug!!

  2. Thankyou a million times for this work-around.
    For those who like me didn’t ‘get it’ at first, the problem is that you can select items in the combo by using the keyboard arrows and until it has been dropped down it allows the disabled items to be selected that way. I was probably a bit dim not understanding it, so let me repay you by adding my bit of information.

    By default, in a list of strings, you can also select the items by typing the first character into the combo, unless it is editable. Disabled items are selectable this way. My work-around for this is to handle the PreviewTextInput event. Eventargs e.Text contains the text you typed, and you can implement the usual functionality yourself, excluding disabled items, and cancel the default processing by setting e.Handled to true. The usual functionality is to select the next item that starts with the typed letter. I would include the code but I can’t see how to do it in a nicely formatted way.

    I think this is another bug.

    Do you know, is there a list of known bugs for WPF 4 and their workarounds? Particularly DataGrid, which is really buggy and I am tearing my hair out. Not a good look.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s