naacal
naacal

Reputation: 620

How to DataBind to specific item properties in an ItemsControl in the most elegant way?

Scenario: A ListView is DataBound to an ObservableCollection<CustomClass> and is displaying it's items through a custom ItemTemplate. The CustomClass just contains three string properties and one boolean property and already has INotifyPropertyChanged implemented on every of it's four properties. The custom ItemTemplate of the ListView has One-Way bindings on the three string properties and a Two-Way binding on the boolean property, displaying it as a CheckBox.

Problem: I'm looking for the most elegant (in terms of WPF) way to display the count of all checked items in that ListView using a TextBlock - or in other words, all items that have their boolean property set to true in that collection. I want that TextBlock to immediately update the displayed count if one of the ListView items gets checked/unchecked. I know that there are (rather) ugly ways to achieve this with code behind and eventhandling, but I'd like to know if there's a clever way to do this maybe completely in XAML with arcane DataBinding syntax.

Edit: Just as an example/clarification: The ListView displays 100 items, 90 items have their boolean property set to true, so the TextBlock will display '90'. If the user unchecks one more item through it's CheckBox and therefore sets it's property to false through the Two-Way binding, the TextBlock should update to '89'.

Upvotes: 2

Views: 335

Answers (4)

naacal
naacal

Reputation: 620

Thanks for all the answers I've got, these were all applicable solutions but unfortunately not really what I've tried to achieve. So this is how I've solved the problem now:

I've implemented a DependencyProperty on the Window containing the TextBlock:

public static readonly DependencyProperty ActiveItemCountProperty =
        DependencyProperty.Register("ActiveItemCount", typeof(int), typeof(CustomControl), new UIPropertyMetadata(0));

On the DataTemplate for the ListView items the CheckBox registered an EventHandler for the Click-Event:

<CheckBox IsChecked="{Binding Active, Mode=TwoWay}" Click="CheckBox_Click" />

The event handler in code behind looks something like this:

    private void CheckBox_Click(object sender, RoutedEventArgs e)
    {
        ObservableCollection<CustomClass> sourceCol = listView.DataContext as ObservableCollection<CustomClass>;
        if (sourceCol != null)
            ActiveItemCount = sourceCol.Count(x => x.Active);
    }

And obviously, the TextBlock is just data bound to this DependencyProperty:

<TextBlock Text="{Binding Path=ActiveItemCount, ElementName=ControlRoot}" />

With ControlRoot being the name of the Window.

Upvotes: 0

Jehof
Jehof

Reputation: 35544

You could use a Converter to build up a string with the count of the checked items

public sealed class CountToStringConverter : System.Windows.Data.IValueConverter {
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        ObservableCollection<CustomClass> items = value as ObservableCollection<CustomClass>;

        int count = 0;

        foreach (var item in items) {
            if (item.IsChecked) {
                count++;
            }
        }

        return count + " Items";
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        throw new NotImplementedException();
    }

    #endregion
}

Bind the Text-Property of the TextBox to the Collection.

<TextBox Text={Binding Items, Converter={StaticResource countToStringConverter}}/>

UPDATE: This Binding works only if the Property Items fires the PropertyChanged-Event, if the Collection is changed.

Upvotes: 2

Simon Steele
Simon Steele

Reputation: 11608

Personally I would probably perform this in my ViewModel. Subscribe to the property changed on the items in the ObservableCollection, and then signal the Count property changed on the ViewModel whenever the boolean property changes. In your view simply bind to the Count property.

Upvotes: 2

KeithS
KeithS

Reputation: 71573

If it were a simple ASP.NET form, I'd look at using JQuery to count the selected items in the ListBox. That may still be a viable option in WPF:

var count = 0;
$('#multiItemListBox :selected').each(count++);

Plug this code into a JS event handler for the OnChange event of the ListBox. You'll have to know what the ListBox would actually be called in the HTML the client gets, and I'm not sure how WPF mashes them up or how to stick the correct reference into server-side XAML.

Upvotes: 0

Related Questions