Сумма GroupStyle не обновляется, когда несколько элементов - PullRequest
0 голосов
/ 12 мая 2009

Я успешно применил объясненный трюк здесь . Но у меня все еще есть одна проблема.

Краткий обзор: я отображаю пользователей в ListView. Пользователи перегруппированы по Странам, и в шаблоне данных GroupStyle I отображается сумма всех связанных с группой Users.Total с использованием конвертера. Но пользователи пользовательского интерфейса могут изменять значение свойства «Всего» пользователей через модальное окно.

Если в группе есть только один элемент, отображаемая сумма пользователя и сумма корректно обновляются. Но когда в группе несколько элементов, обновляется только общее количество пользователей (посредством привязки), но даже тот конвертер, который должен сделать сумму (TotalSumConverter), не вызывается!

У вас есть идеи, откуда это может взяться? Должен ли я использовать какой-то триггер, чтобы убедиться, что конвертер вызывается при изменении элементов?

Ответы [ 3 ]

4 голосов
/ 11 февраля 2010

Проблема в том, что преобразователь значений, который вычисляет сумму для всех элементов в группе, не запускается при изменении элемента, так как для измененных элементов нет уведомления. Одним из решений является привязка к чему-то другому, что вы можете контролировать, как он делает уведомления, и уведомлять заголовок группы при необходимости.

Ниже приведен рабочий пример. Вы можете изменить количество пользователей в текстовом поле, и итоги будут пересчитаны.

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();
        }
    }
}
0 голосов
/ 17 мая 2009

Проблема с обновлением представления состоит в том, что оно полностью обновляет его, и у меня есть расширители для моей группировки, которые вернутся в исходное состояние, даже если пользователь их закрыл. Так что да, это возможный обходной путь, но это не совсем удовлетворительно.

Кроме того, ваше объяснение DependencyObject интересно, но тогда почему оно работает, когда в моей группе только один элемент?

0 голосов
/ 12 мая 2009

Уловка, которую вы используете, связывает нижний колонтитул группы с ListView.Items, который не будет обновлять ваше представление автоматически, как, например, DependencyObject. Вместо этого принудительно обновляйте после каждого обновления значение Total, как это:

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