0

ここで説明したトリックを適用することに成功しました。しかし、私にはまだ 1 つの問題があります。

簡単な要約: ListView にユーザーを表示します。ユーザーは国ごとに再グループ化され、GroupStyle DataTemplate では、コンバーターを使用して、Users.Total に関連するすべてのグループの合計を表示します。ただし、UI ユーザーは、モーダル ウィンドウを使用して、Users の "Total" プロパティ値を変更できます。

グループに項目が 1 つしかない場合、表示されるユーザー合計と合計の両方が適切に更新されます。しかし、グループ内に複数の項目がある場合、(バインドによって) ユーザーの合計のみが更新されますが、合計を作成するはずのコンバーター (TotalSumConverter) は呼び出されません!

それがどこから来たのか、何か分かりますか?アイテムに変更があったときにコンバーターが呼び出されるようにするために、何らかのトリガーを使用する必要がありますか?

4

3 に答える 3

4

問題は、グループ内のすべてのアイテムの合計を計算する値コンバーターが、アイテムが変更されたときに実行されないことです。これは、変更されたアイテムの通知がないためです。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();
        }
    }
}
于 2010-02-11T16:28:16.917 に答える
0

使用しているトリックは、グループ フッターを ListView.Items にデータバインドします。これは、たとえば DependencyObject のように、ビューを自動的に更新しません。代わりに、次のように Total を更新するたびに更新を強制します。

CollectionViewSource viewSource = FindResource("ViewSource") as CollectionViewSource;
viewSource.View.Refresh();
于 2009-05-12T17:10:01.423 に答える
0

ビューを更新する際の問題は、ビューが完全に更新されることです。グループ化用のエキスパンダーがあり、ユーザーがビューを閉じても元の状態に戻ります。はい、それは可能な回避策ですが、完全に満足できるものではありません。

また、あなたの DependencyObject の説明は興味深いですが、グループに項目が 1 つしかないのになぜ機能するのでしょうか?

于 2009-05-17T11:07:01.363 に答える