4

我有一个 ObservableCollection,我需要绑定到 2 个标签,首先显示集合中的项目数,然后显示值的总和。

第一个标签绑定到集合计数属性,第二个标签直接绑定到 ObservableCollection,使用转换器计算所有项目的总数

XAML 看起来像这样

<Grid>
    <ListBox Name="itemList" ItemsSource="{Binding DataList}"/>
    <Label Name="lblcount" Content="{Binding DataList.Count}" />
    <Label Name="lblTotal" Content="{Binding DataList, Converter={StaticResource calculateTotalConvertor}" />
</Grid>

我的虚拟机有一个这样的集合

    ObservableCollection<int> data = new ObservableCollection<int>();

    public ObservableCollection<int> DataList
    {
        get { return data; }
        set { data = value; }
    }

我的转换器代码是

public class CalculateTotalConvertor : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ObservableCollection<int> collection = value as ObservableCollection<int>;

        return collection.Sum();
    }

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

问题是在 DataList、ListView 中添加新项目,并且显示项目计数的标签会更新,但“lblTotal”不会随总数更新。

基本上如何强制您的绑定在 ObservableCollection 更改时进行评估?它如何直接用于 ListView 或 DataGrid 但不适用于 label ?

我知道这个问题可以通过在 VM 中创建一个属性来解决,以在集合更新时显示总数并引发属性更改,但是还有比这更好的解决方案吗?

当然,这是我实际问题的简化形式,我无法访问 ViewModel 和集合,它是第三方控件。我正在创建一个包装用户控件,并与视图与其内部集合进行相对绑定。

4

3 回答 3

3

其他答案正确解释了为什么它没有更新。要强制它更新,您可以将转换器更改为IMultiValueConverter

public class CalculateTotalConvertor : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        ObservableCollection<int> collection = values.FirstOrDefault() as ObservableCollection<int>;

        return collection.Sum();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后将您的绑定更改为MultiBinding也拉入计数的 a:

<Label Name="lblTotal">
    <Label.Content>
        <MultiBinding Converter="{StaticResource calculateTotalConvertor}">
            <Binding Path="DataList"/>
            <Binding Path="DataList.Count"/>
        </MultiBinding>
    </Label.Content>
</Label>

现在第二个绑定将在添加或删除项目时通知绑定需要更新,但您可以忽略计数值而不使用它。

于 2013-02-06T20:54:25.363 回答
0

它没有更新,因为它绑定到DataList并且DataList没有更改,计数标签更新,因为DataList.Count当项目添加到列表时它的绑定更新。

我能想到的更新Sum标签的唯一方法是通知 UIDataList已更改,但这将导致 ListBox 重新绑定列表,并且它的性能将比仅在模型更新上拥有一个属性要昂贵得多Sum. _

所以我认为最好的选择是使用模型上的属性来计算总和,使用ObservableCollections CollectionChangedEventor 在将项目添加到列表的逻辑中

于 2013-02-06T10:47:28.267 回答
0

它适用于ListViewand DataGrid,因为这些是ItemsControl侦听ObservableCollection's 的 s CollectionChangedEvent,当通过添加或删除项目更改集合本身时会引发它。

Label另一方面是ContentControl只听的PropertyChangedEventObservableCollection由于插入后的DataList 与之前相同,因此不会引发任何事件。

刚刚看到你的编辑:

CollectionChangedEvent如果您正在创建一个包装控件,请为第 3 方控件命名,并从您的控件后面的代码中连接到其内部集合。这样您仍然可以将更新通知推送到您的包装视图。

使用额外的属性,它将为您节省一些转换器上的代码。从后面的代码:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    ObservableCollection<int> _list = new ObservableCollection<int>();
    int _sum = 0;
    Random rnd = new Random();

    public MainWindow()
    {
        DataList = new ObservableCollection<int>();
        DataList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DataList_CollectionChanged);
        DataContext = this;
        InitializeComponent();
    }

    void DataList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (object number in e.NewItems)
                    _sum += (int)number;
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (object number in e.OldItems)
                    _sum -= (int)number;
                break;
        }
        OnNotifyPropertyChanged("Sum");
    }

    public int Sum { get { return _sum; } }
    public ObservableCollection<int> DataList { get; set; }

    private void Add_Btn_Click(object sender, RoutedEventArgs e)
    {
        DataList.Add(rnd.Next(0, 256));
    }

    private void Remove_Btn_Click(object sender, RoutedEventArgs e)
    {
        if (DataList.Count == 0)
            return;

        DataList.RemoveAt(DataList.Count - 1);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnNotifyPropertyChanged(string property)
    {
        if (PropertyChanged == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
}
于 2013-02-06T11:10:49.573 回答