65.9K
CodeProject is changing. Read more.
Home

WPF Disabled Items Behaviour

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11 votes)

Aug 6, 2016

CPOL

1 min read

viewsIcon

9143

downloadIcon

64

This tip presents a way to have disabled items in your ComboBox

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