zoukankan      html  css  js  c++  java
  • 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。日积月累,水滴石穿!

  • 相关阅读:
    Hackerspace
    删除指定的多个文件
    windows 复制 文本文件内容 到剪切板
    Two-Factor Authentication 2FA
    Carriage-Return Line-Feed
    外观模式(Facade) Adapter及Proxy 设计模式之间的关系 flume 云服务商多个sdk的操作 face

    A good example is a User-Agent switcher which changes User-Agent on every request:
    Colly provides a clean interface to write any kind of crawler/scraper/spider
    java的(PO,VO,TO,BO,DAO,POJO)解释
  • 原文地址:https://www.cnblogs.com/527289276qq/p/4379494.html
Copyright © 2011-2022 走看看