Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF CheckListBox/RadioListBox + CheckComboBox/RadioComboBox

0.00/5 (No votes)
31 Oct 2008 1  
Intended for LOB applications: WPF CheckListBox/RadioListBox with bound datatype.

Introduction

In LOB applications, we have to design many windows, and many entities are bound to entry fields. Also, sometimes, there are enums or bool[] arrays to be used. Therefore, I had a look at the chances I got to bind some user control to an enum data type. There is no CheckListBox as in WinForms, and WPF shows the whole power when a ListBox has to be customized to a CheckListBox. A template-style will do for that more or less. It is more difficult when the CheckListBox should be bound to a flagged enum or is a RadioListBox.

WPFCheckListBox_Sample.png

What I wanted to achieve

In the case of showing all enum items to the user, I wanted to do the following:

  • drop a control from the toolbox on the form
  • set a binding to the entity, specifying the property name

With declaring the property in the business object as an enum type, I already do everything beforehand, so I do not have to think again about the enum items and setting the ItemSource, and so on.

Using the code

Like the specs, you just drop the desired control onto the form and set the binding. In the sample, all the necessary parts are contained to show the functionality:

<clb:RadioListBox Name="listBoxType" 
                  CheckedValue="{Binding PropType, Mode=TwoWay}"/>

<clb:CheckListBox Name="listBoxFeature"
                  CheckedValue="{Binding PropFeature, Mode=TwoWay}"/>

The CheckedValue dependency property holds the bound data property from the entity. It is important to specify that the binding is TwoWay.

Sometimes, you don't have an enum data type but a bool[] array or anything else. In this case, there is no way to find out what the items to appear in the selection list are. So, you must somehow declare and propagate the collection to the control. You can do this in the code or in XAML. In any case, this has to be done before the first binding occurs. Here is a sample in XAML:

<Window.Resources>
    <coll:ArrayList x:Key="actionList">
        <clb:CheckItem KeyValue="0" Display="action 0"/>
        <clb:CheckItem KeyValue="1" Display="action 1"/>
        <clb:CheckItem KeyValue="2" Display="action 2"/>
    </coll:ArrayList>

    <coll:ArrayList x:Key="arrayList">
        <clb:CheckItem KeyValue="0" Display="item 0"/>
        <clb:CheckItem KeyValue="1" Display="item 1"/>
        <clb:CheckItem KeyValue="2" Display="item 2"/>
        <clb:CheckItem KeyValue="3" Display="item 3"/>
    </coll:ArrayList>
</Window.Resources>

<clb:RadioListBox Name="listBoxAction" 
                  CheckListArray="{StaticResource actionList}" 
                  CheckedValue="{Binding PropAction, Mode=TwoWay}" />

<clb:CheckListBox Name="listBoxArray"
                  CheckListArray="{StaticResource arrayList}" 
                  CheckedValue="{Binding PropArray, Mode=TwoWay}"/>

The CheckListArray holds the collection to be shown in the list. Specify the KeyValue if you want the chosen value as, e.g., an integer.

What is happening when running

In the underlying base class of the CheckListBox and RadioListBox, the DataContextChanged is used to intercept on a binding to the bound property. In this case, the CheckedValue.OnDataContextChanged, the type of the bound property, is evaluated, and either a prepared collection, or the items of the enum are bound to the ItemSource of the ListBox. To keep track of the changes, a ViewModel (I call it this way, might not meet the name) is created to control the ListBox and the bound property.

This class is important, the enum could be of various types: byte, int, uint, long, ulong... They all work neat because the ViewModel uses a UInt64 array of values to keep track of the enumeration values. The corresponding data types are boxed/unboxed accordingly.

The [Flags] attribute is very important on an enum data type. Without this, only one choice can be checked, and even a CheckListBox is used.

If someone wants to do all this before the first binding, the method CreateViewModel could be called manually by specifying the corresponding type.

public void CreateViewModel(Type boundType)
{
   if (boundType==null)
   {
      return;
   }
   if (this.CheckViewModel == null || this.CheckViewModel.BoundType != boundType)
   {
      Type _viewBase = typeof(CheckViewModel<>);
      Type _viewType = _viewBase.MakeGenericType(boundType);

      this.CheckViewModel = (ICheckViewModel)Activator.CreateInstance(_viewType);
      this.CheckViewModel.HostParent = this;
      this.CheckViewModel.IsRadioMode = this.isRadioMode;  // is passed by constructor

      if (this.CheckListArray != null)
      {
          List<CheckItem> _itemList = new List<CheckItem>();
          foreach (CheckItem _checkItem in this.CheckListArray)
          {
             _itemList.Add(_checkItem);
          }
          this.CheckViewModel.CheckItems = _itemList;
      }
      else
      {
          this.CheckViewModel.InitDiscovery();
      }
   }
   if (this.CheckViewModel != null && this.ItemsSource == null)
   {
      this.ItemsSource = this.CheckViewModel.CheckItems;
      this.DisplayMemberPath = "Display";
      this.SelectedValuePath = "KeyValue";
      this.SelectionMode = SelectionMode.Single;
   }
}

Overview

The CheckModelView is encapsulated in the CheckListBoxBase. From this base class, the CheckListBox and the RadioListBox are derived. The main reason for this is the different template they use for the ListBox:

CheckViewModel.png

So maybe, it helps someone, or some real WPF-experts (you name them) might come up with a better solution. Cheers!

History

  • 15.10.2008 > Initial publication.
  • 31.10.2008 > Update: CheckComboBox and RadioComboBox.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here