0%

   在wpf中虽然ObservableCollection作为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
{ ///


/// MainWindow.xaml 的交互逻辑 ///

public partial class MainWindow : Window
{ public MainWindow()
{
InitializeComponent(); #region 基础数据(员工集合) ObservableCollection employeeList = new ObservableCollection { 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;
} ///
/// 右键菜单、按住Ctrl键可多选 ///

///
///
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);
}
}
}
} ///
/// 关键字改变时触发 ///

///
///
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
} ///


/// 根据关键字(工号或姓名)筛选员工 ///

///
///
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);
}
}
} ///
/// 清空关键字 ///

///
///
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的方法:

///


/// 根据关键字(工号或姓名)筛选员工 ///

///
///
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

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

///


/// 关键字改变时触发 ///

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