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.
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!!
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.
I am happy that the post is useful. There is no list of known bugs with work-arounds published so I guess the best bet is to use the usual sources, including blog posts. You could also have a look at WPF Connect at http://connect.microsoft.com/WPF.
Link appreciated. I will check it out soon.