【问题标题】:How to Group and Sort Objects in ObjectListView?如何在 ObjectListView 中对对象进行分组和排序?
【发布时间】:2026-01-30 12:20:03
【问题描述】:

我正在尝试将我的对象列表分组到 ObjectListView

ObjectListView 应根据第一列对对象进行分组,然后根据自定义排序对同一列进行排序。

我该怎么做?我已经阅读了ObjectListView 的文档: http://objectlistview.sourceforge.net/cs/gettingStarted.html#gettingstarted

到目前为止,我已经实现了自定义排序,但我不确定如何触发分组?请记住,我试图在第一列上进行分组,然后应用自定义排序。

我的自定义排序依赖于 BeforeSorting 事件:

// after initializing components
olv.BeforeSorting += olv_BeforeSorting;

那么……

private void olv_BeforeSorting(object sender,BrightIdeasSoftware.BeforeSortingEventArgs e)
{
        olvDataSource.Sort((x, y) => x.Group.ID.CompareTo(y.Group.ID));
        e.Handled = true;
}

ObjectListView 显示我的有序对象列表,但它没有组合在一起。每个对象都显示在自己的行上,没有组标题。

排序后如何对对象进行分组?

【问题讨论】:

  • “组”由什么组成?它似乎是按 ID 排序的,为了进一步分类到“组”(无论它们可能是什么),您可以在按 ID 排序时在那里进行。
  • 啊,好吧。因此,与其排序,我如何对它们进行分组,然后使用自定义排序对组进行排序?
  • 目前还不清楚是什么问题。我建议创建一个Minimal, Reproducible Example WITH DATA。我还建议仔细查看ObjectListView 文档,当单击列标题时,您描述的排序似乎是自动完成的。
  • 因此,您的代码(连接 BeforeSort 事件并按 ID 排序)似乎是不必要的。单击 ID 列标题时,ObjectListView 应该会自动执行此操作。 ShowGroups 似乎只是按单击的列标题的第一个字符“分组”数据。在不知道您要达到的目标的情况下,可能很难获得答案。
  • @JohnG 感谢您的跟进。我可以看到我的问题不是很具有描述性。我将添加更多细节并尝试澄清它。

标签: c# winforms sorting grouping objectlistview


【解决方案1】:

您可以按如下方式强制分组列:

olv.ShowGroups = true;
olv.AlwaysGroupByColumn = olvColumn1;

如果您想在列中显示一个值并按不同的值分组,您可以使用GroupByKeyGetter

olvColumn1.GroupKeyGetter = GroupKeyGetter;

委托类似于:

private object GroupKeyGetter(object rowObject)
{
    var o = rowObject as MyClass;

    if(o == null)
        return "unknown";

    return o.ID;
}

有些东西在你打电话之前不会生效

olv.RebuildColumns();

始终按(任意函数)排序

如果您想强制对某些自定义逻辑进行排序,您可以在 BeforeSorting 事件中使用 ListViewItemSorter。这类似于注册CustomSorter(但当ShowGroups 为真时这似乎不起作用)。

olv.BeforeSorting += olv_BeforeSorting;

然后

private void olv_BeforeSorting(object sender, BrightIdeasSoftware.BeforeSortingEventArgs e)
{
    //example sort based on the last letter of the object name
    var s = new OLVColumn();
    s.AspectGetter = (o) => ((MyClass)o).Name.Reverse().First(); 

    this.olv.ListViewItemSorter = new ColumnComparer(
                s, SortOrder.Descending, e.ColumnToSort, e.SortOrder);
    e.Handled = true;
}

【讨论】:

  • 感谢您的建议。我实现了你的代码,但是一旦我添加了我的排序事件(可能是因为 e.Handled = true; 没有分组,但只要我注释掉那行,我就会得到我的组。现在的问题 是不是我的组没有按照我想要的顺序排序。关于我可以做些什么来让分组(自定义)按照我需要的方式排序有什么想法吗?
  • 如果您注释掉 olvDataSource.Sort... 位但留下 e.Handled = true 会发生什么?也许这有副作用? olvDataSource 是什么类型?通常你不会对集合进行排序,而是对 olv 控件本身进行排序。
  • 我已经更新了答案,说明我将如何在保持组的同时强制执行自定义排序。看看这是否适合您作为对感觉不对的 olvDataSource 进行排序的替代方法。
  • 感谢您的跟进。我实施了您的建议,但排序接缝覆盖了分组。我对所有行进行了排序,但没有分组。一切都显示为没有组标题的单独行。有一个GroupFormatter 应用于分组列,这会干扰该过程吗?老实说,这是我正在尝试修改的预先存在的代码。所以这对我来说是一次学习经历。
  • 那是奇怪的行为,我添加了一个 GroupFormatter 并且我无法复制该行为(格式化程序工作正常并且排序不会破坏任何东西)。这几乎让我认为消失的组是其他东西的副作用,比如在排序后建立组时抛出异常。我喜欢您将比较器解决方案放入感觉正确的组格式化程序中(例如使用 API 而不是反对它)。
【解决方案2】:

我将这个分享给任何可能遇到这里的人,他们正在寻找一种方法来对 ObjectListView 中的组应用自定义排序。

可能有更好的方法来做到这一点,但这种方法对我有用。

colFirst.GroupFormatter = (BrightIdeasSoftware.OLVGroup group, BrightIdeasSoftware.GroupingParameters parms) =>
{
    ObjectA a = (OjectA)group.Key;

    /* Add any processing code that you need */

    group.Task = " . . . ";
    group.Header = "Special Name: " + a.Name;
    group.Subtitle = $("Object A: {a.Index}, Total Water Consumption: {a.WaterConsumption}");

    // This is what is going to be used as a comparable in the GroupComparer below
    group.Id = a.ID;

    // This will create the iComparer that is needed to create the custom sorting of the groups
    parms.GroupComparer = Comparer<BrightIdeasSoftware.OLVGroup>.Create((x, y) => (x.GroupId.CompareTo(y.GroupId)));
};

OLVColumn.GroupFormatter 在这里简单解释一下: http://objectlistview.sourceforge.net/cs/recipes.html#how-do-i-put-an-image-next-to-a-group-heading

【讨论】:

    【解决方案3】:

    这很有效,基本上是这里的食谱中描述的http://objectlistview.sourceforge.net/cs/recipes.html?highlight=sort#how-can-i-change-the-ordering-of-groups-or-rows-within-a-group

    首先订阅 olv BeforeCreatingGroups 事件。

    然后在事件处理程序中创建一个自定义排序比较器。在这种情况下,对于匹配“Turtles”的组,它将推到排序的末尾,但显然你可以在其中包含任何你想要的复杂逻辑。

    private void Olv_BeforeCreatingGroups(object sender, CreateGroupsEventArgs e)
    {
        e.Parameters.GroupComparer = Comparer<BrightIdeasSoftware.OLVGroup>.Create(
            (x, y) => (
                x.Header == "Turtles" ? 1
                    : x.GroupId.CompareTo(y.GroupId)
            )
        );
    }
    

    这是我最初使用的,因为它是食谱中的内容。但我最终切换到更像 Marwan 的答案,因为它创建了一个空间来重新配置组标题本身。

    【讨论】: