MVVM模式下如何在ListView中对项目进行分组

17

我不明白我做错了什么。我想在listview中对项进行分组。 结果应该看起来像这样:

输入图像描述

我正在使用MVVM模式。这是我的XAML代码。

<CollectionViewSource x:Key="EmploeeGroup"                               
                      Source="{Binding Path=AllEmploees}">
  <CollectionViewSource.GroupDescriptions>
    <PropertyGroupDescription PropertyName="FirstName" />
  </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

<ListView AlternationCount="2" 
          DataContext="{StaticResource EmploeeGroup}"
          ItemsSource="{Binding IsAsync=True}" Padding="0,0,0,10">
  <ListView.GroupStyle>
    <GroupStyle>
      <GroupStyle.ContainerStyle>
        <Style TargetType="{x:Type GroupItem}">
          <Setter Property="Margin" Value="0,0,0,5"/>
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type GroupItem}">
                <Expander IsExpanded="True" BorderBrush="#FFA4B97F" 
                                            BorderThickness="0,0,0,1">
                  <Expander.Header>
                    <DockPanel>
                      <TextBlock FontWeight="Bold"
                                 Text="Name: "/>
                      <TextBlock FontWeight="Bold"
                                 Text="{Binding Path=FirstName}"/>
                    </DockPanel>
                  </Expander.Header>
                  <Expander.Content>
                    <ItemsPresenter />
                  </Expander.Content>
                </Expander>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </GroupStyle.ContainerStyle>
    </GroupStyle>
  </ListView.GroupStyle>
  <ListView.View>
    <GridView>
      <GridViewColumn Width="150"
                      Header="FirstName"
                      DisplayMemberBinding="{Binding Path=FirstName}"/>
      <GridViewColumn Width="150"
                      Header="LastName"
                      DisplayMemberBinding="{Binding Path=LastName}"/>
    </GridView>
  </ListView.View>
</ListView>

这是我的EmploeeListViewModel.cs

public class EmploeeListViewModel: ViewModelBase
{
  readonly EmploeeRepository _emploeeRepository;

  private ObservableCollection<EmploeeViewModel> _allmpl;
  public ObservableCollection<EmploeeViewModel> AllEmploees
  {
    get
    {
      if (_allmpl == null)
      {
        _allmpl = new ObservableCollection<EmploeeViewModel>();
        CreateAllEmploee();
      }
      return _allmpl;
    }
  }

  public EmploeeListViewModel(EmploeeRepository emploeeRepository)
  {
    if (emploeeRepository == null)
      throw new ArgumentNullException("emploeeRepository");

    _emploeeRepository = emploeeRepository;
    _emploeeRepository.EmploeeAdded += this.OnEmploeeAddedToRepository;
  }

 private void CreateAllEmploee()
 {
   List<EmploeeViewModel> all =
                (from emploee in _emploeeRepository.GetEmploees()
                 select new EmploeeViewModel(emploee)).ToList();
   foreach (EmploeeViewModel evm in all)
   {
     evm.PropertyChanged += this.OnEmploeeViewModelPropertyChanged;
     AllEmploees.Add(evm);
   }
   this.AllEmploees.CollectionChanged += this.OnCollectionChanged;
 }

 //this.OnCollectionChanged;
 //this.OnEmploeeViewModelPropertyChanged;
}

EmploeeViewModel.cs

public class EmploeeViewModel : ViewModelBase
{
  #region Fields
    Emploee _emploee;
    bool _isSelected;
  #endregion

  #region Constructor
    public EmploeeViewModel(Emploee emploee)
    {
      if (emploee == null)
        throw new ArgumentNullException("emploee");
      this._emploee = emploee;
    }
  #endregion

  #region Emploee Properties
    public bool IsSelected
    {
      get { return _isSelected; }
      set
      {
        if (value == _isSelected)
          return;

        _isSelected = value;
        base.OnPropertyChanged("IsSelected");
      }
    }

    public string FirstName
    {
      get { return _emploee.FirstName; }
      set
      {
        if (value == _emploee.FirstName)
          return;
        _emploee.FirstName = value;
        base.OnPropertyChanged("FirstName");
      }
    }

    public string LastName
    {
      get { return _emploee.LastName; }
      set
      {
        if (value == _emploee.LastName)
          return;
        _emploee.LastName = value;
        base.OnPropertyChanged("LastName");
      }
    }
  #endregion
}
  • 为什么我无法将"FirstName"属性与Expander.Header的TextBlock绑定?
  • 如果我在Expander.Header中写入Text="{Binding}",为什么会有MS.Internal.Data.CollectionViewGroupInternal对象类型?

我应该如何更改我的XAML或.CS代码才能产生这些结果

2个回答

26

我自己在这个问题上找到了答案。

传递到转换器中的对象的类型为:MS.Internal.Data.CollectionViewGroupInternal。

使用“Name”来绑定分组名称的主要原因是,该属性包含了当前“组集合”拥有的名称(根据您指定的GroupDescription)。

在PropertyGroupDescription中,GropertyName不重要。在GroupStyle容器中,您必须始终使用 {Binding Path=Name}。

我只需要更改我的XAML中的一个字符串。

从:

<TextBlock FontWeight="Bold" Text="{Binding Path=FirstName}"/>

收件人:

<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>

1
这太奇怪了。我在这篇文章中注意到了它:http://www.wpf-tutorial.com/listview-control/listview-grouping/,并认为这可能是一个打字错误。为什么它总是应该是“Name”,而不是实际绑定的名称!!!干杯,伙计。 - Mehrad
2
“Name”是保存你分组的项目的属性名称。你有一组项目,所有这些项目都共享一个名字,而“Name”属性保存了这个共同的值。当你对复杂类型进行分组时,这一点变得更加明显。 - John Melville
我花了一个半小时来弄清楚为什么我的组名没有显示(但组本身是正确的),现在看到你的答案,你救了我。我认为这非常重要。 - MwBakker

0

最近遇到了与“姓名/名字”绑定问题相同的问题,并在这里找到了我的项目的解决方案: Grouping ListView WPF

简而言之,在Expander标记内,您可以将DataContext设置为“{Binding Items}”。之后,您可以使用原始属性名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接