wpf CollectionViewSource与ListBox的折叠、分组显示,及输入关键字 Filter的筛选

原文: wpf CollectionViewSource与ListBox的折叠、分组显示,及输入关键字 Filter的筛选

   在wpf中虽然ObservableCollection<T>作为ListBox的Itemsource,很好,很强大!但是CollectionViewSource与ListBox才是天作之合!

wpf中ListBox支持分组显示,CollectionViewSource.GroupDescriptions为其实现了分组。废话不多说,下面上ListBox分组显示的Demo代码:

XAML:

<Window x:Class="WpfListGroup.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
         xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
        Title="MainWindow" Height="450" Width="525">
    <Window.Resources>
        <CollectionViewSource x:Key="employeeCollectionViewSource" Filter="employeeCollectionViewSource_Filter">
            <CollectionViewSource.SortDescriptions>
                <!--排序描述-->
                <scm:SortDescription PropertyName="Num"/>
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <!--分组描述-->
                <PropertyGroupDescription PropertyName="Title"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>

        <Style x:Key="ButtonFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#F3F3F3" Offset="0"/>
            <GradientStop Color="#EBEBEB" Offset="0.5"/>
            <GradientStop Color="#DDDDDD" Offset="0.5"/>
            <GradientStop Color="#CDCDCD" Offset="1"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/>
        <Style x:Key="nocheckedButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid Width="29.72">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="contentPresenter">
                                                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                                                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="90"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ellipse">
                                                <EasingColorKeyFrame KeyTime="0" Value="#FF2CA50B"/>
                                            </ColorAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Pressed"/>
                                    <VisualState x:Name="Disabled"/>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Unfocused"/>
                                    <VisualState x:Name="Focused"/>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ValidationStates">
                                    <VisualState x:Name="Valid"/>
                                    <VisualState x:Name="InvalidFocused"/>
                                    <VisualState x:Name="InvalidUnfocused"/>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Ellipse x:Name="ellipse" Fill="#FF75AB80" Margin="0" Stroke="{x:Null}" VerticalAlignment="Stretch" Width="16" Height="16"/>
                            <Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome"  SnapsToDevicePixels="true"  >
                                <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RenderTransformOrigin="0.5,0.5">
                                    <ContentPresenter.RenderTransform>
                                        <TransformGroup>
                                            <ScaleTransform/>
                                            <SkewTransform/>
                                            <RotateTransform/>
                                            <TranslateTransform/>
                                        </TransformGroup>
                                    </ContentPresenter.RenderTransform>
                                </ContentPresenter>
                            </Microsoft_Windows_Themes:ButtonChrome>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="false"/>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DockPanel Grid.Row="0" LastChildFill="True" >
            <TextBlock VerticalAlignment="Center" DockPanel.Dock="Left" Text="搜索:"/>
            <Button  Content="  ×  " VerticalAlignment="Center" DockPanel.Dock="Right" 
                   Background="White" BorderBrush="{x:Null}" Margin="0" 
                      Style="{DynamicResource nocheckedButtonStyle}"
                     HorizontalAlignment="Right" 
                     FontFamily="Forte" Foreground="White" ToolTip="清空"
                    Click="btnClearKeyword_Click"/>
            <TextBox x:Name="txtEmployeeKeyword"  VerticalAlignment="Center" TextChanged="txtEmployeeKeyword_TextChanged" />
        </DockPanel>
        <ScrollViewer x:Name="scv1" Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <ListBox x:Name="lbx1" SelectionMode="Extended"  ItemsSource="{Binding Source={StaticResource ResourceKey=employeeCollectionViewSource}}">
                <!--分组样式-->
                <ListBox.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.ContainerStyle>
                            <Style TargetType="{x:Type GroupItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type GroupItem}">
                                            <Expander>
                                                <Expander.Header>
                                                    <Grid>
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="Auto"/>
                                                            <ColumnDefinition/>
                                                        </Grid.ColumnDefinitions>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition/>
                                                        </Grid.RowDefinitions>
                                                        <StackPanel Orientation="Horizontal" Margin="0,0,10,0">
                                                            <!--分组的组名-->
                                                            <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
                                                            <!--该分组元素(员工)的总和数-->
                                                            <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, StringFormat=(共{0}条)}"/>
                                                        </StackPanel>
                                                        <Line Grid.Column="1" SnapsToDevicePixels="true"  X1="0" X2="1" Stretch="Fill"  StrokeThickness="1"/>
                                                    </Grid>
                                                </Expander.Header>
                                                <ItemsPresenter />
                                            </Expander>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </GroupStyle.ContainerStyle>
                    </GroupStyle>
                </ListBox.GroupStyle>
                <!--右键菜单-->
                <ListBox.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Show" Click="MenuItem_Click"/>
                    </ContextMenu>
                </ListBox.ContextMenu>
                <!--“没有”绑定ListBox.ItemTemplate,是因为在Employee类重写了ToString()方法-->
            </ListBox>
        </ScrollViewer>
        <ScrollViewer x:Name="scv2" Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Visibility="Collapsed">
            <ListBox Name="lbx2" ItemsSource="{Binding Source={StaticResource employeeCollectionViewSource}}" SelectionMode="Extended">   <!--按Ctrl键可多选-->
                <ListBox.ContextMenu>
                    <ContextMenu>
                        <!--右键菜单-->
                        <MenuItem Header="Show" Click="MenuItem_Click"/>
                    </ContextMenu>
                </ListBox.ContextMenu>
            </ListBox>
        </ScrollViewer>
    </Grid>
</Window>
View Code

 


C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfListGroup
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            #region 基础数据(员工集合)
            ObservableCollection<Employee> employeeList = new ObservableCollection<Employee>
            { 
               new Employee{EmployeeNum="0027",EmployeeName="张三",Sex="",Title="副经理"},
               new Employee{EmployeeNum="1086",EmployeeName="春丽",Sex="",Title="秘书"},
               new Employee{EmployeeNum="1031",EmployeeName="王五",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1211",EmployeeName="赵阳",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1201",EmployeeName="孙迪",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1416",EmployeeName="李玥玥",Sex="",Title="秘书"},
               new Employee{EmployeeNum="0017",EmployeeName="钱哆哆",Sex="",Title="副经理"},
               new Employee{EmployeeNum="1016",EmployeeName="周畅",Sex="",Title="秘书"},
               new Employee{EmployeeNum="1231",EmployeeName="郑超",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1131",EmployeeName="王思聪",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1871",EmployeeName="李文",Sex="",Title="普通员工"},
               new Employee{EmployeeNum="1266",EmployeeName="周琪妹",Sex="",Title="秘书"}
            }; 
            #endregion

            CollectionViewSource employeeCvs = (CollectionViewSource)this.FindResource("employeeCollectionViewSource");
            employeeCvs.Source = employeeList;
        }
        /// <summary>
        /// 右键菜单、按住Ctrl键可多选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            //获取关键字
            string keyword = txtEmployeeKeyword.Text.Trim();
            if (string.IsNullOrEmpty(keyword))//如果没有关键字
            {
                if (lbx1.SelectedItem != null)//判断lbx1有没有选中项
                {
                    foreach (var item in lbx1.SelectedItems)
                    {
                        Employee employee = item as Employee;
                        string msg = string.Format("姓名:{0},工号:{1},性别:{2},职位:{3}", employee.EmployeeName, employee.EmployeeNum, employee.Sex, employee.Title);
                        MessageBox.Show(msg);
                    }
                }
            }
            else
            {
                if (lbx2.SelectedItem != null)//有关键字的话,显示lbx2
                {
                    foreach (var item in lbx2.SelectedItems)//判断lbx2有没有选中项
                    {
                        Employee employee = item as Employee;
                        string msg = string.Format("姓名:{0},工号:{1},性别:{2},职位:{3}", employee.EmployeeName, employee.EmployeeNum, employee.Sex, employee.Title);
                        MessageBox.Show(msg);
                    }
                }
            }
        }

        /// <summary>
        /// 关键字改变时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtEmployeeKeyword_TextChanged(object sender, TextChangedEventArgs e)
        {
            string keyword = txtEmployeeKeyword.Text.Trim();
            if (string.IsNullOrEmpty(keyword))//无关键字,显示scv1下的listbox(有分组)
            {

                scv1.Visibility = Visibility.Visible;
                scv2.Visibility = Visibility.Collapsed;
            }
            else//有关键字,显示scv2下的listbox(无分组)
            {
                scv1.Visibility = Visibility.Collapsed;
                scv2.Visibility = Visibility.Visible;
            }
            CollectionViewSource employeeCvs = (CollectionViewSource)this.FindResource("employeeCollectionViewSource");
            employeeCvs.View.Refresh();//刷新View
        }
        /// <summary>
        /// 根据关键字(工号或姓名)筛选员工
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void employeeCollectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            string keyword = txtEmployeeKeyword.Text.Trim();
            Employee employee = e.Item as Employee;
            if (employee != null)
            {
                if (string.IsNullOrEmpty(keyword))//无关键字,直接Accept
                {
                    e.Accepted = true;
                }
                else
                {
                    //有关键字、筛选员工号或姓名中包含关键字的员工
                    e.Accepted = employee.EmployeeNum.Contains(keyword) || employee.EmployeeName.Contains(keyword);
                }
            }
        }
        /// <summary>
        /// 清空关键字
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearKeyword_Click(object sender, RoutedEventArgs e)
        {
            this.txtEmployeeKeyword.Clear();
        }
    }

    public class Employee:INotifyPropertyChanged
    {
        #region 实现更改通知
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        } 
        #endregion

        /// <summary>
        /// 重载ToString()方法
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.EmployeeNum + "  " + this.EmployeeName;
        }

        private string title;
        /// <summary>
        /// 职位
        /// </summary>
        public string Title
        {
            get { return title; }
            set { title = value;
            RaisePropertyChanged("Title");
            }
        }
        
        private string employeeName;
        /// <summary>
        /// 姓名
        /// </summary>
        public string EmployeeName
        {
            get { return employeeName; }
            set { employeeName = value;
            RaisePropertyChanged("EmployeeName");
            }
        }
        private string employeeNum;
        /// <summary>
        /// 工号
        /// </summary>
        public string EmployeeNum
        {
            get { return employeeNum; }
            set { employeeNum = value;
            RaisePropertyChanged("EmployeeNum");
            }
        }
        private string sex;
        /// <summary>
        /// 性别
        /// </summary>
        public string Sex
        {
            get { return sex; }
            set { sex = value;
            RaisePropertyChanged("Sex");
            }
        }

    }
}
View Code

运行效果:

右键菜单点击“Show” 弹出选中项的员工信息:

 

输入关键字"同步"筛选模糊查询员工:

 

点击清空按钮清空关键字,“恢复”分组数据:

 

总结核心xaml:

①资源CollectionViewSource, CollectionViewSource.GroupDescriptions:分组描述(依据),CollectionViewSource.SortDescriptions:分组排序(描述)

在资源中:

  <CollectionViewSource x:Key="employeeCollectionViewSource" Filter="employeeCollectionViewSource_Filter">
            <CollectionViewSource.SortDescriptions>
                <!--排序描述-->
                <scm:SortDescription PropertyName="Num"/>
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <!--分组描述-->
                <PropertyGroupDescription PropertyName="Title"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
View Code

②绑定到ListBox的Itemsource上,设置分组样式,使用Expander控件使分组可以折叠:

    <ScrollViewer x:Name="scv1" Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <ListBox x:Name="lbx1" SelectionMode="Extended"  ItemsSource="{Binding Source={StaticResource ResourceKey=employeeCollectionViewSource}}">
                <!--分组样式-->
                <ListBox.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.ContainerStyle>
                            <Style TargetType="{x:Type GroupItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type GroupItem}">
                                            <Expander>
                                                <Expander.Header>
                                                    <Grid>
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="Auto"/>
                                                            <ColumnDefinition/>
                                                        </Grid.ColumnDefinitions>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition/>
                                                        </Grid.RowDefinitions>
                                                        <StackPanel Orientation="Horizontal" Margin="0,0,10,0">
                                                            <!--分组的组名-->
                                                            <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
                                                            <!--该分组元素(员工)的总和数-->
                                                            <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, StringFormat=(共{0}条)}"/>
                                                        </StackPanel>
                                                        <Line Grid.Column="1" SnapsToDevicePixels="true"  X1="0" X2="1" Stretch="Fill"  StrokeThickness="1"/>
                                                    </Grid>
                                                </Expander.Header>
                                                <ItemsPresenter />
                                            </Expander>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </GroupStyle.ContainerStyle>
                    </GroupStyle>
                </ListBox.GroupStyle>
                <!--右键菜单-->
                <ListBox.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Show" Click="MenuItem_Click"/>
                    </ContextMenu>
                </ListBox.ContextMenu>
                <!--“没有”绑定ListBox.ItemTemplate,是因为在Employee类重写了ToString()方法-->
            </ListBox>
        </ScrollViewer>
View Code


总结核心C#:

①CollectionViewSource的筛选器Filter的方法:

 /// <summary>
        /// 根据关键字(工号或姓名)筛选员工
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void employeeCollectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            string keyword = txtEmployeeKeyword.Text.Trim();
            Employee employee = e.Item as Employee;
            if (employee != null)
            {
                if (string.IsNullOrEmpty(keyword))//无关键字,直接Accept
                {
                    e.Accepted = true;
                }
                else
                {
                    //有关键字、筛选员工号或姓名中包含关键字的员工
                    e.Accepted = employee.EmployeeNum.Contains(keyword) || employee.EmployeeName.Contains(keyword);
                }
            }
        }
View Code

②关键字文本框的文本发生改变时触发的事件:

/// <summary>
        /// 关键字改变时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtEmployeeKeyword_TextChanged(object sender, TextChangedEventArgs e)
        {
            string keyword = txtEmployeeKeyword.Text.Trim();
            if (string.IsNullOrEmpty(keyword))//无关键字,显示scv1下的listbox(有分组)
            {

                scv1.Visibility = Visibility.Visible;
                scv2.Visibility = Visibility.Collapsed;
            }
            else//有关键字,显示scv2下的listbox(无分组)
            {
                scv1.Visibility = Visibility.Collapsed;
                scv2.Visibility = Visibility.Visible;
            }
            CollectionViewSource employeeCvs = (CollectionViewSource)this.FindResource("employeeCollectionViewSource");
            employeeCvs.View.Refresh();//刷新View
        }
View Code

总结:以上就是ListBox的分组、折叠、筛选显示的Demo。日积月累,水滴石穿!

 

posted on 2019-01-16 10:02 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10275467.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值