WPF给ListView增加点击标题栏排序的功能

最近因为需要,给一个模块的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);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容