【问题标题】:GroupStyle sum not updated when multiple items多个项目时 GroupStyle 总和未更新
【发布时间】:2010-10-25 13:49:30
【问题描述】:

我已经成功应用了here 解释的技巧。但是我还有一个问题。

快速回顾:我在 ListView 中显示用户。用户按国家/地区重新分组,在 GroupStyle DataTemplate 中,我使用转换器显示所有与组相关的 Users.Total 的总和。但 UI 用户可以通过模态窗口更改用户的“Total”属性值。

当组中只有一项时,显示的用户总数和总和都会正确更新。但是当组中有多个项目时,仅更新用户总数(通过绑定),但甚至不调用应该进行总和的转换器(TotalSumConverter)!

你知道它可能来自哪里吗?我应该使用某种触发器来确保在项目中有修改时调用转换器吗?

【问题讨论】:

    标签: wpf listview binding grouping converter


    【解决方案1】:

    问题在于,当项目发生更改时,计算组中所有项目总和的值转换器不会运行,因为没有通知更改项目。一种解决方案是绑定到其他东西,您可以控制它如何通知并在需要时通知组头。

    下面是一个工作示例。您可以在文本框中更改用户的计数,然后重新计算总数。

    XAML:

    <Window x:Class="UserTotalTest.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:userTotalTest="clr-namespace:UserTotalTest"
        Title="Window1" Height="300" Width="300"
        Name="this">
    
        <Window.Resources>
    
            <userTotalTest:SumConverter x:Key="SumConverter" />
    
            <CollectionViewSource Source="{Binding Path=Users}" x:Key="cvs">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="Country"/>
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>
    
        </Window.Resources>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="10" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <ListView 
                Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                ItemsSource="{Binding Source={StaticResource cvs}}">
                <ListView.View>
                    <GridView>
                        <GridView.Columns>
                            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" />
                            <GridViewColumn Header="Count" DisplayMemberBinding="{Binding Path=Count}" />
                        </GridView.Columns>
                    </GridView>
                </ListView.View>
                <ListView.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.ContainerStyle>
                            <Style TargetType="{x:Type GroupItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type GroupItem}">
                                            <StackPanel Margin="10">
                                                <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
                                                <ItemsPresenter />
                                                <TextBlock FontWeight="Bold">
                                                    <TextBlock.Text>
                                                        <MultiBinding Converter="{StaticResource SumConverter}">
                                                            <MultiBinding.Bindings>
                                                                <Binding Path="DataContext.Users" ElementName="this" />
                                                                <Binding Path="Name" />
                                                            </MultiBinding.Bindings>
                                                        </MultiBinding>
                                                    </TextBlock.Text>
                                                 </TextBlock>
                                            </StackPanel>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </GroupStyle.ContainerStyle>
                    </GroupStyle>
                </ListView.GroupStyle>
            </ListView>
            <ComboBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" 
                ItemsSource="{Binding Path=Users}"
                DisplayMemberPath="Name" 
                SelectedItem="{Binding Path=SelectedUser}" />
            <TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Path=SelectedUser.Country}" />
            <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Path=SelectedUser.Count}" />
        </Grid>
    </Window>
    

    后面的代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    
    namespace UserTotalTest
    {
        public partial class Window1 : Window
        {
            public Window1() 
            {
                InitializeComponent();
    
                DataContext = new UsersVM();
            }
        }
    
        public class UsersVM : INotifyPropertyChanged
        {
            public UsersVM()
            {
                Users = new List<User>();
                Countries = new string[] { "Sweden", "Norway", "Denmark" };
                Random random = new Random();
                for (int i = 0; i < 25; i++)
                {
                    Users.Add(new User(string.Format("User{0}", i), Countries[random.Next(3)], random.Next(1000)));
                }
    
                foreach (User user in Users)
                {
                    user.PropertyChanged += OnUserPropertyChanged;
                }
    
                SelectedUser = Users.First();
            }
    
            private void OnUserPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "Count")
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Users"));
                }
            }
    
            public List<User> Users { get; private set; }
    
            private User _selectedUser;
            public User SelectedUser
            {
                get { return _selectedUser; }
                set 
                {
                    _selectedUser = value; if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("SelectedUser"));
                    }
                }
            }
    
            public string[] Countries { get; private set; }
    
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
        }
    
        public class User : INotifyPropertyChanged
        {
            public User(string name, string country, double total)
            {
                Name = name;
                Country = country;
                Count = total;
            }
    
            public string Name { get; private set; }
            private string _country;
            public string Country
            {
                get { return _country; }
                set
                {
                    _country = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Country"));
                    }
                }
            }
    
            private double _count;
            public double Count
            {
                get { return _count; }
                set
                {
                    _count = value; if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Count"));
                    }
                }
            }
    
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
        }
    
        public class SumConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                IEnumerable<User> users = values[0] as IEnumerable<User>;
                string country = values[1] as string;
                double sum = users.Cast<User>().Where(u =>u.Country == country).Sum(u => u.Count);
                return "Count: " + sum;
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      您使用的技巧将组页脚数据绑定到 ListView.Items 不会自动更新您的视图,例如 DependencyObject 所做的那样。而是在每次更新 Total 后强制刷新,如下所示:

      CollectionViewSource viewSource = FindResource("ViewSource") as CollectionViewSource;
      viewSource.View.Refresh();
      

      【讨论】:

        【解决方案3】:

        刷新视图的问题在于它完全刷新了它,而且我的分组有扩展器,即使用户关闭它们,它们也会扩展回原来的状态。所以是的,这是一种可能的解决方法,但并不完全令人满意。

        您的 DependencyObject 解释也很有趣,但是,为什么当我的组中只有一个项目时它会起作用?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-10-06
          • 1970-01-01
          • 2014-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-05
          • 1970-01-01
          相关资源
          最近更新 更多