我已经成功应用了here解释的技巧。但我还有一个问题。
快速回顾:我在ListView中显示用户。用户按国家/地区重新分组,在GroupStyle DataTemplate中,我使用转换器显示所有组相关Users.Total的总和。但UI用户可以通过模态窗口更改用户的“总计”属性值。
当组中只有一个项目时,显示的用户总数和总和都会正确更新。但是当组中有多个项目时,只更新用户总数(通过绑定),但是甚至不会调用应该进行总和的转换器(TotalSumConverter)!
你知道它可能来自哪里吗?我应该使用某种触发器来确保在项目中有修改时调用Converter吗?
答案 0 :(得分:4)
问题是,当项目发生更改时,计算组中所有项目的总和的值转换器不会运行,因为没有更改项目的通知。一种解决方案是绑定到其他可以控制通知的方式,并在需要时通知组头。
以下是一个工作示例。您可以在文本框中更改用户的计数,并重新计算总计。
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();
}
}
}
答案 1 :(得分:0)
您正在使用的技巧将组页脚数据绑定到ListView.Items,它不会自动更新您的视图,例如DependencyObject。而是在每次更新Total之后强制刷新,如下所示:
CollectionViewSource viewSource = FindResource("ViewSource") as CollectionViewSource;
viewSource.View.Refresh();
答案 2 :(得分:0)
刷新视图的问题是它完全刷新它,并且我有用于分组的扩展器,即使用户关闭它们,它也会扩展回原始状态。所以是的,它是一种可能的解决方法,但它并不完全令人满意。
你的DependencyObject解释也很有趣,但是,当我的小组中只有一个项目时,为什么它会起作用呢?