Introduction
It was requested that some items in a ComboBox
list would not be selectable. This proved to be a much more difficult task than I expected. IsHitTestVisible
and IsEnabled
for a control used in the ItemTemplate
did not work.
Design
I initially started testing in the OnSelectionChanged
event, and then moved to a generic, and finally a behaviour:
public class ComboBoxItemAvailableBehaviour
{
public static readonly DependencyProperty IsAvailableProperty =
DependencyProperty.RegisterAttached("IsAvailable", typeof(Func<object,bool>),
typeof(ComboBoxItemAvailableBehaviour), new UIPropertyMetadata(null, OnValueChanged));
public static Func<object, bool> GetIsAvailable(Control o)
{
return (Func<object, bool>)o.GetValue(IsAvailableProperty);
}
public static void SetIsAvailable(Control o, Func<object, bool> value)
{
o.SetValue(IsAvailableProperty, value);
}
private static void OnValueChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
var comboBox = dependencyObject as ComboBox;
if (comboBox != null)
{
comboBox.SelectionChanged -= ComboBox_SelectionChanged;
comboBox.SelectionChanged += ComboBox_SelectionChanged;
}
}
private static void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = (ComboBox)sender;
if (e.AddedItems.Count > 0)
{
if (! GetIsAvailable(comboBox).Invoke(e.AddedItems[0]))
if (e.RemovedItems.Count > 0)
comboBox.SelectedItem = e.RemovedItems[0];
else
comboBox.SelectedIndex = -1;
}
}
}
The DependencyProperty
is of type Func<object,bool>
so that any value can be passed to the behavior for evaluation. This Func<object,bool>
is used to determine if the DataContext
for the particular ComboBoxItem
is selectable. When a new Func<object,bool>
is provided, a subscription is made to the SelectionChanged
event. When the selection is changed, checking for any added item is made, and if there is an added item, the function is called to check if this is an item is available. If it is not available, the change is backed out. This could mean that the SelectedItem
is set back to the removed item, or the SelectedIndex
is set to -1
if there are no removed items.
In the OnValueChanged
method, before there is s subscription to the SelectionChanged
event
, there is a remove to ensure that there is not a multiple subscription to the event
. Probably unnessary since this behaviour will probably not change, but method to be used to check if the item is availble could be updated.
Using the Code
To use the code, a property of type Func<object, bool>
has to be in the DataContext
. This is bound to the IsAvailable DependencyProperty
of the behaviour:
<ComboBox Width="100"
HorizontalAlignment="Center"
VerticalAlignment="Center" SelectionChanged="Selector_OnSelectionChanged"
local:ComboBoxItemAvailableBehaviour.IsAvailable="{Binding IsAvailable}"
ItemsSource="{Binding ItemsSource}"
SelectedItem="{Binding SelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:ComboBoxItemViewModel}">
<TextBlock Foreground="{Binding IsAvailable,
Converter={local:IsTrueConverter},
ConverterParameter=Black:Gray}"
IsHitTestVisible="False"
Text="{Binding Text}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
History
- 08/05/2016: Initial version