在wpf中虽然ObservableCollection
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
}; #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。日积月累,水滴石穿!