views:

71

answers:

4

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'.

A: 

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.

KeithS
Thanks for your answer, but I don't just simply want to display the count of selected items (I could simply bind to SelectedItems.Count for that), but to display the count of all items that have a specific property (in this case a boolean property) set to a specific value. For example: The ListView has 100 items, 90 of these items have their boolean property set to true - so the TextBlock will display '90'. If I uncheck one item it displays 89, but the ListView itself still has all 100 initial items.
naacal
+2  A: 

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.

Jehof
+2  A: 

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.

Simon Steele
+1. I think this is a better solution than mine.
Jehof
Thank you for your answer. Yes indeed, extending the ViewModel is a nice way to do this. But I kinda wanted to avoid this due to the very simple data structures I'm working with... I've been thinking about implementing an EventSetter on the DataTemplate to handle the Checked-Event of each item's CheckBox from code behind... but what I'd really love to do is with some kind of direct DataBinding over the ItemsSource of the ListView or an ObjectDataProvider that utilizes a LINQ-statement or something like that...
naacal
@naacal. It´s easier to provide an additional property with the count of checked items, than to use EventSetter, Checked-Event and so on.
Jehof
@Jehof: It may be easy indeed, but I've got to implement another class for the sole purpose of providing this property to me. Right now, the CustomClass is not intended to be part of a ViewModel and that is kinda the whole purpose of my inital question - is there any way to do this in XAML without implementing more code just for this small DataBinding to the TextBlock. The DataBinding syntax in a Master/Detail implementation using the special slash (/) syntax to bind to the current item gave me the idea that there may be a way to do something similar over the ItemsSource and the bool property.
naacal
A: 

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.

naacal
i would really caution against this if you want to expand your application in the future, creating a view model and following M-V-VM practices really do so save you time and effort in the long run of an app
David Rogers
forgot to mention that this is also prone to fail if you want to uncheck an item from your code. I would really go with Simon's answer as it is a better solution
David Rogers
Checking/Unchecking works just fine, since I'm recalculating the count on every click on the CheckBox. I agree that implementing a ViewModel might be the better solution in the long run, but the whole scenario is so simple that I find the implementation of additional classes just oversized.
naacal