views:

1010

answers:

2

Hi,

I need to implement CheckBoxList control with ItemsSource and CheckedItems properties. Items from ItemsSource should be displayed as checked checkboxes if CheckedItems contains these values or unchecked otherwise. Also I need two-way databinding support for CheckedItems property (value of this property should be updated when user clicks on checkboxes).

Here some code which probably can help to understand my problem

XAML:

<UserControl x:Class="Namespace.Controls.CheckBoxList" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    <ListBox x:Name="LayoutRoot">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox Content="{Binding}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

Code behind:

public partial class CheckBoxList : UserControl
{
    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CheckBoxList), null);
    public static readonly DependencyProperty CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof(IEnumerable), typeof(CheckBoxList), null);

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }
    public IEnumerable CheckedItems
    {
        get { return (IEnumerable)GetValue(CheckedItemsProperty); }
        set { SetValue(CheckedItemsProperty, value); }
    }

    public CheckBoxList()
    {
        InitializeComponent();
        LayoutRoot.SetBinding(ItemsControl.ItemsSourceProperty, new Binding("ItemsSource") { Source = this });
    }
}

I think that I need to bind ListBox to UserControl with custom converter, which will return collection of items with additional IsChecked property, but it works only in case of one-way data binding.

Looks like I need two-way binding to two properties at one time, but I don't know how to implement it and will appreciate any help with this issue.

Thanks in advance.

A: 

Add an observable collection for your list box datasource to your datacontext:

private ObservableCollection<MyItem> _myItems;
public ObservableCollection<MyItem> MyItems
{
    get { return _searchByFields; }
    set
    {
        _myItems = value;
    }
}

Add a class to hold the data about your checkboxes:

public class MyItem
{
  public bool Checked {get; set; }
  public string MyItemValue { set ; set; }
}

Then in your data template bind listbox to the collection and your data template checkboxes to the respective MyItem properties:

<UserControl x:Class="Namespace.Controls.CheckBoxList"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;    
    <ListBox x:Name="LayoutRoot"
             DataContext="[Dataconext here]"
             ItemsSource={Binding MyItems}>        
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" 
                          Content="{Binding MyItemValue}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

Don't forget to set the DataContext of the binding to the appropriate class (you might be doing this in the XAML or the code behind perhaps)

HTH,
Mark

Mark Cooper
Thanks, but your solution doesn't update CheckedItems property after changing checkbox state.
altso
This assumes that the Items from the collection have some "checked" property that indicates that they have been selected. Whilst its not clear whether that would work fro the OPs actual task, it doesn't work for the problem presented in the question. If you have a simple list of say Cities why should a City object care whether is "Checked" or not. That not it problem, checked by who, for what reason? What if there are more than one control that may select from Cities but each want to describe their own distinct subset.
AnthonyWJones
To update the property make sure the binding is TwoWay
Mark Cooper
If you don't want to have the IsChecked property on your "City" object, then you can add that with a partial class, or extension methods, or have a totally seperated class for your UI state and then convert that in the business logic into and out of your business class.Any more information on the setup you have (maybe MVVM or RIA services?)
Mark Cooper
@Mark: Still requires a separate source list for every selected list.
AnthonyWJones
+2  A: 

First of all you should consider deriving from ListBox rather than UserControl. The ListBox already does most of what you want.

Secondly consider one way binding to an IList. You can then add and remove entires to that IList as the respective items are selected.

Rather than try to bind a CheckBox control in an Item Template you make a copy of the ListBox styles, place them in Generic.xaml as the style of your new control. Then modify the unselected and selected visual states using a checked and unchecked check box as part of the visual appearance.

Now you can attach to the SelectionChanged event and use the Event args AddedItems list to add to the bound IList and the RemovedItems list to remove items from the bound list.

You would need to clear and re-add the set of items to the list box SelectedItems list when either your CheckedItems is assigned or the ItemsSource is changed.

There are probably a number gotchas that you will need to work round but this seems like a more direct path to your goal than starting from scratch with a UserControl base.

AnthonyWJones
Thanks a lot! If I got your idea correctly (map check state of check box to selection state of list item), then it will be possible to use SelectedItems instead of CheckedItems. I don't see any need to handle SelectionChanged event in this case. Also I'm new to Silverlight, so modifying default ListBox style is not trivial for me :)
altso
Ooops, SelectedItems is not a dependency property. Need to handle SelectionChanged as you say.
altso
Yes you can't assign to the SelectedItems property either, so you need to bridge the SelectedItems property of the ListBox with a new property you are exposing which allows the consumer to set an IList for the control to manipulate. Once the initial SelectedItems is set on assignment to your property you can keep the two lists in sync via the SelectionChanged event.
AnthonyWJones