如何使用 XAML 在 WPF 中按首字母对 ListBoxItems 进行分组?

发布于 2024-10-04 03:32:34 字数 5303 浏览 5 评论 0原文

首先,这里是上一篇文章,涉及将 ListBox AccountListBox 数据绑定到 AccountsCollection.cs 类中的 ObservableCollection 帐户。

所以现在我有一个绑定对象 AccountsCollection 和一个名为 AccountTemplate 的 DataTemplate,用于在资源中定义我的 ListBox:

<Window.Resources>
    <controller:AccountsWindowController x:Key="AccountsCollection" />
    <DataTemplate x:Key="AccountTemplate">
        <DockPanel>
            <Button Name="EditButton" 
                    DockPanel.Dock="Right" 
                    Margin="3 0 3 0" 
                    VerticalAlignment="Center" 
                    Content="Edit" />
            <Button Name="DeleteButton" 
                    DockPanel.Dock="Right" 
                    Margin="3 0 3 0" 
                    VerticalAlignment="Center" 
                    Content="Delete" />
            <TextBlock Name="AccountName" 
                       VerticalAlignment="Center" 
                       Text="{Binding Name}" 
                       TextWrapping="NoWrap"
                       TextTrimming="CharacterEllipsis" />
        </DockPanel>
    </DataTemplate>
<Window.Resources>

下面是与 LisBox 本身相关的代码:

<ListBox Name="AccountsListBox" 
         Margin="12,38,12,41" 
         HorizontalContentAlignment="Stretch" 
         ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
         ItemsSource="{Binding Accounts, 
             Source={StaticResource ResourceKey=AccountsCollection}}" 
         ItemTemplate="{StaticResource ResourceKey=AccountTemplate}" 
         MouseDoubleClick="AccountsListBox_MouseDoubleClick">
</ListBox>

我希望我的列表被设计为按起始字母对所有帐户进行分组并显示列表中的字母(我还想对该字母标题应用一些设计)。最终结果应该是这样的:

alt text

感谢您的帮助!

更新:这是成功实现分组的代码。

<Window x:Class="Gui.Wpf.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:entities="clr-namespace:Entities.Accounts;assembly=Entities" 
    xmlns:contollers="clr-namespace:Gui.Wpf.Controllers" 
    xmlns:converters="clr-namespace:Gui.Wpf.Converters" 
    xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
    Title="MainWindow" 
    Width="525" 
    Height="350" >

<Window.Resources>

    <!-- Main window controller -->
    <contollers:MainWindowController 
        x:Key="MainWindowController" />

    <!-- Converter for first letter extraction from the account name -->
    <converters:FirstLetterConverter x:Key="FirstLetterConv" />

    <!-- View source for the AccountsListBox -->
    <CollectionViewSource 
        x:Key="AccountsView" 
        Source="{Binding Accounts, Source={StaticResource ResourceKey=MainWindowController}}">

        <!-- Sorting -->
        <CollectionViewSource.SortDescriptions>
            <componentModel:SortDescription PropertyName="AccountName" />
        </CollectionViewSource.SortDescriptions>

        <!-- Grouping -->
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource ResourceKey=FirstLetterConv}" />
        </CollectionViewSource.GroupDescriptions>

    </CollectionViewSource>

    <!-- Data template for the type Account -->
    <DataTemplate 
        DataType="{x:Type entities:Account}">
        <DockPanel>
            <Button 
                Name="DeleteButton" 
                DockPanel.Dock="Right" 
                Margin="3, 1, 3, 1" 
                VerticalAlignment="Center" 
                Content="Delete" />
            <Button 
                Name="EditButton" 
                DockPanel.Dock="Right" 
                Margin="3, 1, 3, 1" 
                VerticalAlignment="Center" 
                Content="Edit" />
            <TextBlock 
                Name="AccountNameTextBlock" 
                VerticalAlignment="Center" 
                Text="{Binding AccountName}" 
                TextWrapping="NoWrap" 
                TextTrimming="CharacterEllipsis" />
        </DockPanel>

    </DataTemplate>

    <!-- Data template for AccountListBox grouping -->
    <DataTemplate x:Key="GroupingHeader">
        <TextBlock Text="{Binding Path=Name}" Background="Black" Foreground="White" />
    </DataTemplate>

</Window.Resources>

<Grid>
    <ListBox 
        Name="AccountsListBox" 
        Width="300" 
        Height="200" 
        HorizontalAlignment="Center" 
        VerticalAlignment="Center" 
        ItemsSource="{Binding Source={StaticResource ResourceKey=AccountsView}}" 
        HorizontalContentAlignment="Stretch" >
        <ListBox.GroupStyle>
            <GroupStyle 
                HeaderTemplate="{StaticResource ResourceKey=GroupingHeader}" />
        </ListBox.GroupStyle>
    </ListBox>
</Grid>

First, here is the previous post that deals with the ListBox AccountListBox data binding to my ObservableCollection<Account> Accounts from the AccountsCollection.cs class.

So now I have a binding object AccountsCollection and a DataTemplate named AccountTemplate for my ListBox defined in the resources:

<Window.Resources>
    <controller:AccountsWindowController x:Key="AccountsCollection" />
    <DataTemplate x:Key="AccountTemplate">
        <DockPanel>
            <Button Name="EditButton" 
                    DockPanel.Dock="Right" 
                    Margin="3 0 3 0" 
                    VerticalAlignment="Center" 
                    Content="Edit" />
            <Button Name="DeleteButton" 
                    DockPanel.Dock="Right" 
                    Margin="3 0 3 0" 
                    VerticalAlignment="Center" 
                    Content="Delete" />
            <TextBlock Name="AccountName" 
                       VerticalAlignment="Center" 
                       Text="{Binding Name}" 
                       TextWrapping="NoWrap"
                       TextTrimming="CharacterEllipsis" />
        </DockPanel>
    </DataTemplate>
<Window.Resources>

And here is the code related to the LisBox itself:

<ListBox Name="AccountsListBox" 
         Margin="12,38,12,41" 
         HorizontalContentAlignment="Stretch" 
         ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
         ItemsSource="{Binding Accounts, 
             Source={StaticResource ResourceKey=AccountsCollection}}" 
         ItemTemplate="{StaticResource ResourceKey=AccountTemplate}" 
         MouseDoubleClick="AccountsListBox_MouseDoubleClick">
</ListBox>

I want my list to be designed to group all accounts by starting letter and to show that letter in the list (Also I want to apply some design to that letter header). The final result should be something like this:

alt text

Thanks for all the help!

UPDATE: Here's the code with grouping successfully implemented.

<Window x:Class="Gui.Wpf.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:entities="clr-namespace:Entities.Accounts;assembly=Entities" 
    xmlns:contollers="clr-namespace:Gui.Wpf.Controllers" 
    xmlns:converters="clr-namespace:Gui.Wpf.Converters" 
    xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
    Title="MainWindow" 
    Width="525" 
    Height="350" >

<Window.Resources>

    <!-- Main window controller -->
    <contollers:MainWindowController 
        x:Key="MainWindowController" />

    <!-- Converter for first letter extraction from the account name -->
    <converters:FirstLetterConverter x:Key="FirstLetterConv" />

    <!-- View source for the AccountsListBox -->
    <CollectionViewSource 
        x:Key="AccountsView" 
        Source="{Binding Accounts, Source={StaticResource ResourceKey=MainWindowController}}">

        <!-- Sorting -->
        <CollectionViewSource.SortDescriptions>
            <componentModel:SortDescription PropertyName="AccountName" />
        </CollectionViewSource.SortDescriptions>

        <!-- Grouping -->
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource ResourceKey=FirstLetterConv}" />
        </CollectionViewSource.GroupDescriptions>

    </CollectionViewSource>

    <!-- Data template for the type Account -->
    <DataTemplate 
        DataType="{x:Type entities:Account}">
        <DockPanel>
            <Button 
                Name="DeleteButton" 
                DockPanel.Dock="Right" 
                Margin="3, 1, 3, 1" 
                VerticalAlignment="Center" 
                Content="Delete" />
            <Button 
                Name="EditButton" 
                DockPanel.Dock="Right" 
                Margin="3, 1, 3, 1" 
                VerticalAlignment="Center" 
                Content="Edit" />
            <TextBlock 
                Name="AccountNameTextBlock" 
                VerticalAlignment="Center" 
                Text="{Binding AccountName}" 
                TextWrapping="NoWrap" 
                TextTrimming="CharacterEllipsis" />
        </DockPanel>

    </DataTemplate>

    <!-- Data template for AccountListBox grouping -->
    <DataTemplate x:Key="GroupingHeader">
        <TextBlock Text="{Binding Path=Name}" Background="Black" Foreground="White" />
    </DataTemplate>

</Window.Resources>

<Grid>
    <ListBox 
        Name="AccountsListBox" 
        Width="300" 
        Height="200" 
        HorizontalAlignment="Center" 
        VerticalAlignment="Center" 
        ItemsSource="{Binding Source={StaticResource ResourceKey=AccountsView}}" 
        HorizontalContentAlignment="Stretch" >
        <ListBox.GroupStyle>
            <GroupStyle 
                HeaderTemplate="{StaticResource ResourceKey=GroupingHeader}" />
        </ListBox.GroupStyle>
    </ListBox>
</Grid>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

国产ˉ祖宗 2024-10-11 03:32:34

您可以使用 CollectionViewSource 和转换器来提取第一个字母:

<local:FirstLetterConverter x:Key="firstLetterConverter" />

<CollectionViewSource x:Key="cvs" Source="{Binding Accounts, Source={StaticResource AccountsCollection}}">
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="AccountName" />
    </CollectionViewSource.SortDescriptions>
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource firstLetterConverter}" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

...

<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}">
    ...

转换器:

public class FirstLetterConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string s = value as string;
        if (s != null && s.Length > 0)
            return s.Substring(0, 1);
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

如果要将样式应用于组,可以使用 GroupStyle 属性:

  ...
  <ItemsControl.GroupStyle>
    <GroupStyle>
      <GroupStyle.HeaderTemplate>
        <DataTemplate>
          <TextBlock FontWeight="Bold" FontSize="15" Text="{Binding Path=Name}" />
        </DataTemplate>
      </GroupStyle.HeaderTemplate>
      <GroupStyle.ContainerStyle>
        <Style TargetType="{x:Type GroupItem}">
          <Setter Property="Background" Value="Gray" />
          <Setter Property="Foreground" Value="White" />
        </Style>
      </GroupStyle.ContainerStyle>
    </GroupStyle>
  </ItemsControl.GroupStyle>
  ...

You can use a CollectionViewSource, and a converter to extract the first letter:

<local:FirstLetterConverter x:Key="firstLetterConverter" />

<CollectionViewSource x:Key="cvs" Source="{Binding Accounts, Source={StaticResource AccountsCollection}}">
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="AccountName" />
    </CollectionViewSource.SortDescriptions>
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="AccountName" Converter="{StaticResource firstLetterConverter}" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

...

<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}">
    ...

Converter:

public class FirstLetterConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string s = value as string;
        if (s != null && s.Length > 0)
            return s.Substring(0, 1);
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

If you want to apply a style to the group, you can use the GroupStyle property:

  ...
  <ItemsControl.GroupStyle>
    <GroupStyle>
      <GroupStyle.HeaderTemplate>
        <DataTemplate>
          <TextBlock FontWeight="Bold" FontSize="15" Text="{Binding Path=Name}" />
        </DataTemplate>
      </GroupStyle.HeaderTemplate>
      <GroupStyle.ContainerStyle>
        <Style TargetType="{x:Type GroupItem}">
          <Setter Property="Background" Value="Gray" />
          <Setter Property="Foreground" Value="White" />
        </Style>
      </GroupStyle.ContainerStyle>
    </GroupStyle>
  </ItemsControl.GroupStyle>
  ...
别忘他 2024-10-11 03:32:34

下面是一个非常相似的解决方案示例:

首先,我们需要为您的 DataContext 生成一个更好的集合 - 这是一个您可以根据您的目的轻松修改的示例 -

public Window1()
{
    InitializeComponent();
    var s = new[] { "Dave", "Adam", "Jeny", "Nick", "James" };
    DataContext = s
        .Select(n => n[0])
        .Distinct()
        .ToDictionary(l => l.ToString(), l => s
            .Where(w => w
                .StartsWith(l.ToString())));
}

然后我们只需要嵌套的 ItemsControls对于用户界面 -

<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
  <DataTemplate>
    <StackPanel>
      <TextBlock Foreground="Red" Text="{Binding Key}" FontSize="12" Margin="5" />
      <ItemsControl ItemsSource="{Binding Value}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
              <Button Content ="View" Margin="0,0,5,0" />
              <Button Content ="Delete"  Margin="0,0,5,0" />
              <TextBlock Text="{Binding}" />
            </StackPanel>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </StackPanel>
  </DataTemplate>
</ItemsControl.ItemTemplate>

我们得到这个:

alt text

Here is an example of a solution that is very similar:

Firstly, we need to generate a better collection for your DataContext - here's an example that you could easily modify for your purposes -

public Window1()
{
    InitializeComponent();
    var s = new[] { "Dave", "Adam", "Jeny", "Nick", "James" };
    DataContext = s
        .Select(n => n[0])
        .Distinct()
        .ToDictionary(l => l.ToString(), l => s
            .Where(w => w
                .StartsWith(l.ToString())));
}

then we just need nested ItemsControls for the UI -

<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
  <DataTemplate>
    <StackPanel>
      <TextBlock Foreground="Red" Text="{Binding Key}" FontSize="12" Margin="5" />
      <ItemsControl ItemsSource="{Binding Value}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
              <Button Content ="View" Margin="0,0,5,0" />
              <Button Content ="Delete"  Margin="0,0,5,0" />
              <TextBlock Text="{Binding}" />
            </StackPanel>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </StackPanel>
  </DataTemplate>
</ItemsControl.ItemTemplate>

and we get this:

alt text

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文