最近因为需要,给一个模块的ListView添加一个点击标题栏排序的功能,添加了以后对数据筛选和分类显示就更方便了。
直奔主题,首先要注意,视图必须是GridView
才行,XAML里的ListView是这样的:
<ListView x:Name="lvIPs" ItemsSource="{Binding Path=FilteredIPAssignments}" DockPanel.Dock="Top" PreviewMouseDown="ListView_PreviewMouseDown" d:ItemsSource="{d:SampleData ItemCount=5}" >
<ListView.View>
<GridView>
<GridViewColumn x:Name="colActive" Width="80">
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<EventSetter Event="Click" Handler="GridViewColumnHeader_Click" />
</Style>
</GridViewColumn.HeaderContainerStyle>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName=colActive, Path=ActualWidth}">
<CheckBox Content="Active" IsChecked="{Binding UIIsActive}"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="IP Address" DisplayMemberBinding="{Binding Path=UIIPAddress}" Width="120">
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<EventSetter Event="Click" Handler="GridViewColumnHeader_Click" />
</Style>
</GridViewColumn.HeaderContainerStyle>
</GridViewColumn>
在上面的示例代码中,演示了两种不同方式定义的列,最关键的部分是每一个GridViewColumn中定义的这个列头的单击事件:
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<EventSetter Event="Click" Handler="GridViewColumnHeader_Click" />
</Style>
</GridViewColumn.HeaderContainerStyle>
列标题单击事件及排序的代码如下:
#region 点击标题头排序
private GridViewColumnHeader _lastHeaderClicked = null;
private ListSortDirection _lastDirection = ListSortDirection.Ascending;
private void GridViewColumnHeader_Click(object sender, RoutedEventArgs e)
{
var headerClicked = e.OriginalSource as GridViewColumnHeader;
if (headerClicked?.Column == null) return;
string sortBy = (headerClicked.Column.DisplayMemberBinding as Binding)?.Path.Path;
//GridViewColumn 中使用的是 CellTemplate,而不是 DisplayMemberBinding,这一点会导致点击列头时 sortBy 为空
//而对于CellTemplate定义的列,数据绑定可能有多个绑定,无法自动获取,所以这里对CellTemplate定义的列单独处理
if (string.IsNullOrEmpty(sortBy))
{
sortBy = GetBindingPathFromCellTemplate(headerClicked.Column);
if (string.IsNullOrEmpty(sortBy)) return; // 无法识别绑定路径
//特殊处理
//if (sortBy == "UICategory") sortBy = "CategoryDisplay";
}
ListSortDirection direction = ListSortDirection.Ascending;
if (headerClicked == _lastHeaderClicked && _lastDirection == ListSortDirection.Ascending)
direction = ListSortDirection.Descending;
ICollectionView dataView = CollectionViewSource.GetDefaultView(lvIPs.ItemsSource);
dataView.SortDescriptions.Clear();
dataView.SortDescriptions.Add(new SortDescription(sortBy, direction));
dataView.Refresh();
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
#region 用于取得数据绑定设置及恢复排序设定的辅助函数
/// <summary>
/// 清除列表的现有排序设定
/// </summary>
/// <param name="collectionViewSource"></param>
private static void ClearSortSettings(object collectionViewSource)
{
var view = CollectionViewSource.GetDefaultView(collectionViewSource);
if (view != null)
{
view.SortDescriptions.Clear();
view.Refresh();
}
}
/// <summary>
/// 从使用CellTemplate定义的GridViewColumn里取得第一个绑定的数据路径
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
private string GetBindingPathFromCellTemplate(GridViewColumn column)
{
if (column.CellTemplate == null)
return null;
try
{
// 加载模板(虚拟化可视化内容)
var dummyContent = column.CellTemplate.LoadContent();
var bindings = new List<Binding>();
void FindBindings(DependencyObject obj)
{
int count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
foreach (var dp in GetDependencyProperties(child))
{
var expr = BindingOperations.GetBinding(child, dp);
if (expr != null)
{
bindings.Add(expr);
}
}
FindBindings(child);
}
}
FindBindings(dummyContent);
// 取第一个绑定
return bindings.FirstOrDefault()?.Path?.Path;
}
catch
{
return null;
}
}
/// <summary>
/// 辅助函数:获取一个元素的所有 DependencyProperty
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private IEnumerable<DependencyProperty> GetDependencyProperties(object element)
{
var properties = new List<DependencyProperty>();
var type = element.GetType();
var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
foreach (var field in fields)
{
if (field.FieldType == typeof(DependencyProperty))
{
if (field.GetValue(null) is DependencyProperty dp)
{
properties.Add(dp);
}
}
}
return properties;
}
#endregion
#endregion
注意:
上面代码中的备注特殊处理
处,可以根据具体需要,可能特殊情况下的取得的sortBy不是所需要的内容,就人工处理一下。
//特殊处理
if (sortBy == "UICategory") sortBy = "CategoryDisplay";
代码里的函数ClearSortSettings
是用来清除列表的排序设定的,如果排序后更改了列表中的数据,可是显示的数据排序仍然是上次点击列表标题设置的,不想要这样排序想恢复默认的话,使用这个函数对列表进行一次排序设置的复位就可以了。
比如:
//清除上次的排序设定
ClearSortSettings(FilteredIPAssignments);