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。日積月累,水滴石穿!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM