ListBox组中的垂直分隔符

Viv*_*rav 8 c# wpf listbox

我有一个ListBox基于这样的属性进行分组的地方:

CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(listbox.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("CurrentDate");
view.GroupDescriptions.Add(groupDescription);
Run Code Online (Sandbox Code Playgroud)

分组后我想在组之间添加一个垂直分隔符,我写了一个这样的代码:

<ListBox.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
                    <TextBlock Text="{Binding Path=Name}"
                               FontWeight="Bold"/>
                </StackPanel>
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
        <GroupStyle.Panel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </GroupStyle.Panel>
    </GroupStyle>
</ListBox.GroupStyle>
Run Code Online (Sandbox Code Playgroud)

但它看起来像这样:

在此输入图像描述

虽然我想要一个分隔符完全下降,但当我试图增加分隔符的高度时,项目随之下降.

Grx*_*x70 6

诊断

当你组一个项目ListBox使用CollectionView+ GroupStyle会发生什么是ListBox屏幕列表GroupItem控件,每个代表一组项目.A GroupItem基本上由一个ContentPresenter(用于呈现标题)和一个ItemsPresenter(用于呈现分组项目)放在一个StackPanel.

当您指定时GroupStyle.HeaderTemplate,它将用于ContentTemplate提到的ContentPresenter.因此,如果你增加它的高度,Separator它仍将被包含在ContentPresenter导致它垂直生长,并且物品仍将堆叠在它下面 - 因此你的结果.

为实现目标,您需要做的是重新模板化,GroupItem以便SeparatorContentPresenter和旁边显示ItemsPresenter,然后使用它进行连线GroupStyle.ContainerStyle.为方便起见,我们把它放在ListBox.Resources字典中:

<ListBox (...)>
    <ListBox.Resources>
         <ControlTemplate x:Key="GroupItemTemplate" TargetType="{x:Type GroupItem}">
            <DockPanel>
                <Separator DockPanel.Dock="Left"
                           Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
                <StackPanel>
                    <ContentPresenter /><!-- This will be automatically wired -->
                    <ItemsPresenter Margin="5,0,0,0" /><!-- So will this -->
                </StackPanel>
            </DockPanel>
        </ControlTemplate>
    </ListBox.Resource>
    <ListBox.GroupStyle>
        <GroupStyle>
             <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Template"
                            Value="{StaticResource GroupItemTemplate}" />
                </Style>
            </GroupStyle.ContainerStyle>
            (...)
        </GroupStyle>
    </ListBox.GroupStyle>
    (...)
</ListBox>
Run Code Online (Sandbox Code Playgroud)

请注意,我从标题模板中删除了分隔符.

以下是您可能想要获得的结果(我在周围设置了蓝色边框ListBox以区分#3和#4):

在此输入图像描述

默认情况下,我提供的代码摘录将为您提供#1(所有分隔符在整个范围内垂直拉伸ListBox).

要实现#2(分隔符仅向下拉伸到相应组的最后一项),您应该添加<Setter Property="VerticalAlignment" Value="Top" />GroupStyle.ContainerStyle.或者,您可以将其放在模板DockPanel内部GroupItem.

要获得#3(分隔符伸展到最大组的高度),您应该添加VerticalAlignment="Top"GroupStyle.Panel(VirtualizingStackPanel在您的情况下)内的面板.

最后#4(ListBox本身被限制为最大群体的大小)可以通过将被achievied VerticalAlignment="Top"ListBox本身.


lok*_*ing 2

我无法想象一个开箱即用的解决方案,因为您正在尝试调整组。我做了一个例子,但它无法在不使用分隔符的情况下调整项目区域内的列宽度,但在标题处:

代码隐藏

public partial class Window1  {

    public Window1() {
      InitializeComponent();

      this._items.Add(new Item { Name = "one", DateTime = DateTime.Today });
      this._items.Add(new Item { Name = "two", DateTime = DateTime.Today.Subtract(new TimeSpan(1, 0, 0, 0)) });
      this._items.Add(new Item { Name = "three", DateTime = DateTime.Today.Subtract(new TimeSpan(1, 0, 0, 0)) });
      this._items.Add(new Item { Name = "four", DateTime = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)) });
      this._items.Add(new Item { Name = "five", DateTime = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)) });
      this.DataContext = this;
    }

    private ObservableCollection<Item> _items = new ObservableCollection<Item>();

    public ObservableCollection<Item> Items => _items;

  }


  public abstract class ViewModelBase : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
      this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }

  public class Item : ViewModelBase {
    private string _name;
    private DateTime _dateTime;

    public string Name {
      get {
        return this._name;
      }
      set {
        if (value == this._name)
          return;
        this._name = value;
        this.OnPropertyChanged();
      }
    }

    public DateTime DateTime {
      get {
        return this._dateTime;
      }
      set {
        if (value.Equals(this._dateTime))
          return;
        this._dateTime = value;
        this.OnPropertyChanged();
      }
    }

  }
Run Code Online (Sandbox Code Playgroud)

资源分组

 <Window.Resources>
        <CollectionViewSource x:Key="CollectionViewSource" Source="{Binding Items}">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="DateTime" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>
Run Code Online (Sandbox Code Playgroud)

列表框

<ListBox ItemsSource="{Binding Source={StaticResource CollectionViewSource}}" Width="400" Height="200">
            <ListBox.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <GridViewColumnHeader Content="{Binding Name}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.Panel >
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </ListBox.GroupStyle>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
                    <Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="DarkGray" BorderThickness="0,0,1,0" Margin="-6,-2,-6,-2">
                        <StackPanel Margin="6,2,6,2">
                            <TextBlock Text="{Binding Name}"/>
                        </StackPanel>
                    </Border>

                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
Run Code Online (Sandbox Code Playgroud)

此解决方法的关键是使用GridViewColumnHeaderGroupStyle 作为 HeaderTemplate。

可能更好的解决方案是将 ListBox 更改为 ListView 并将 ListView 的View-Property 设置为GridView。但这需要更改您的数据结构。

笔记

列表框的分组从来都不是为了执行您想要执行的任务。列表框和分组的默认方式是在列表框的内容区域中包含扩展器,如此处所述