简体   繁体   English

如何以最优雅的方式将数据绑定到ItemsControl中的特定项目属性?

[英]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. 场景:一个ListView被DataBound ObservableCollection<CustomClass>到一个ObservableCollection<CustomClass>并通过一个自定义的ItemTemplate显示它的项目。 The CustomClass just contains three string properties and one boolean property and already has INotifyPropertyChanged implemented on every of it's four properties. CustomClass仅包含三个字符串属性和一个布尔属性,并且已经在其四个属性的每一个上实现了INotifyPropertyChanged。 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. ListView的自定义ItemTemplate在三个字符串属性上具有单向绑定,在布尔属性上具有双向绑定,将其显示为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. 问题:我正在寻找最优雅的方式(就WPF而言),以使用TextBlock显示该ListView中所有已检查项目的计数-换句话说,在该集合中所有布尔属性设置为true的项目。 I want that TextBlock to immediately update the displayed count if one of the ListView items gets checked/unchecked. 如果希望ListView项之一被选中/取消选中,我希望TextBlock立即更新显示的计数。 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. 我知道有一些(相当)丑陋的方法可以通过代码和事件处理来实现,但是我想知道是否有一种聪明的方法可以完全在XAML中使用奥秘的DataBinding语法来做到这一点。

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'. 编辑:仅作为示例/说明:ListView显示100个项目,其中90个项目的布尔属性设置为true ,因此TextBlock将显示“ 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'. 如果用户通过其CheckBox取消选中了另一个项目,因此通过双向绑定将其属性设置为false ,则TextBlock应该更新为“ 89”。

You could use a Converter to build up a string with the count of the checked items 您可以使用Converter来建立一个带有已检查项目计数的字符串

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. 更新:此绑定仅在属性项触发PropertyChanged-Event且集合发生更改时才起作用。

Personally I would probably perform this in my ViewModel. 我个人可能会在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. 订阅在ObservableCollection中的项目上更改的属性,然后在布尔属性更改时向ViewModel发出Count属性更改的信号。 In your view simply bind to the Count property. 在您看来,只需绑定到Count属性。

If it were a simple ASP.NET form, I'd look at using JQuery to count the selected items in the ListBox. 如果它是简单的ASP.NET表单,那么我将看一下使用JQuery对ListBox中的选定项目进行计数。 That may still be a viable option in WPF: 在WPF中,这仍然是可行的选择:

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

Plug this code into a JS event handler for the OnChange event of the ListBox. 将此代码插入到ListBox的OnChange事件的JS事件处理程序中。 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. 您将必须知道客户端获取的HTML中实际上将调用ListBox的内容,而且我不确定WPF如何将它们混搭起来或如何将正确的引用粘贴到服务器端XAML中。

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: 我已经在包含TextBlock的窗口上实现了DependencyProperty:

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: 在ListView项的DataTemplate上,CheckBox为Click-Event注册了一个EventHandler:

<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只是绑定到此DependencyProperty的数据:

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

With ControlRoot being the name of the Window. 使用ControlRoot作为窗口的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM