来自单个 DataSource 的多个 DataGridView

Multiple DataGridView from single DataSource

我有一个几乎可以正常工作的 Winform 应用程序,但围绕用户通过 DataGridViews 更新和插入数据的方式以及如何将其反馈到后台的列表中变得越来越复杂。目前,两个 DataGridView 都是从单个列表中填充的,当用户更新单元格时,列表随后会从网格中更新。我更喜欢将列表用作数据源。

非常简化的数据集(服务器、函数、进程)- P.S。我无法更改我的数据集:

Server1, KeepAlive, SQLService
Server1, KeepAlive, AnotherProcess
Server1, Kill, RogueProcess
Server2, KeepAlive, SQLService
Server3, KeepAlive, SQLService

DataGridView1 有 1 列包含不同的服务器名称,DataGridView2 有 2 列包含 DataGridView1 中 selected 服务器的所有功能和进程。用户可以编辑任何单元格,目前我会跟踪每个更改并将其反映回列表中,然后刷新网格。我更愿意使用 DataGridView 数据源对象来处理这个问题。

我的问题是:什么是最合适的数据源设置(即对象列表)?如何从数据源过滤到网格中?

到目前为止,我已经尝试了以下方法。创建配置 class:

class Config
{
    [DisplayName("Server")]
    public string server { get; set; }

    [DisplayName("Function")]
    public string function { get; set; }

    [DisplayName("Process")]
    public string checkType { get; set; }
}

创建一个包含这些配置对象的列表:

List<Config> configurations = new List<Config>();

将列表指定为数据源:

dataGridView1.DataSource = configurations;

这会按预期显示所有三列数据。我如何才能 a) 仅显示服务器列和 b) 仅显示不同的列表?

我猜 DataGridView2 我可以使用 RowFilter 只显示 selected 服务器:

(dataGridView2.DataSource as DataTable).DefaultView.RowFilter = ?

在此先感谢您的帮助!

编辑

我试过使用 LINQ:

dataGridView1.DataSource = configs.Select(o => new { Server = o.server }).ToList();

这有效,但我的网格是只读的,所以我也使用了自定义视图模型:

dataGridView1.DataSource = configs.Select(o => new ServerView() { Server = o.server }).ToList();

这正是我想要的显示方式,但是当我编辑单元格时,所做的更改并未反映在列表中。 LINQ 可以像这样与 DataSource 一起使用吗?

编辑 2

使用 stefankmitph 的示例,我可以通过 SortableBindingList 进行过滤:

SortableBindingList<Config> sortableBindingList = new SortableBindingList<Config>(configs.Where(o => o.server == "Server1").ToList());
BindingSource bindingSource = new BindingSource(sortableBindingList, null);
dataGridView1.DataSource = bindingSource;

这解决了 DataGridView2 的问题,它需要根据 #1 中 selected 的内容进行过滤。但是,我仍然不知道如何只在网格中显示某些列。 #1 应该只有服务器,#2 应该有剩余的两列。 LINQ 查询中的 select 不起作用,因为我正在处理 Config 对象......对吗?

多年来,我一直在与 DataGridView 的许多问题作斗争。这对我来说就是 'best practice':

1a) 我最常将我的数据 (List) 附加到 SortableBindingList(那里有很多示例 here, here and here。选择适合您的。)

假设有一个列表配置;

dataGridView.DataSource = new SortableBindingList<Config>(configurations);

现在您的 DataGridView 可以排序了。

1b) 在过滤 DataSource 时,有很多选项。就我而言,过滤仅适用于带有 DataTable 的 BindingSource 作为 DataSource

假设有一个 DataTable dataTableConfigurations:

BindingSource bindingSource = new BindingSource(dataTableConfigurations, null);
dataGridView.DataSource = bindingSource;
bindingSource.Filter = "Server = 'Server3'";

但我很确定这不适用于作为数据源的对象列表。

你能做什么:

SortableBindingList<Config> sortableBindingList = new SortableBindingList<Config>(configurations);
BindingSource bindingSource = new BindingSource(sortableBindingList, null);
dataGridView.DataSource = bindingSource;

这样可以很容易地跟踪数据的变化。 (f.e。BindingSource.Current returns 您的 DataGridView 的当前项目)

BindingSource bindingSource = dataGridView.DataSource as BindingSource;
Config currentConfig = bindingSource.Current as Config;

如果我现在必须过滤数据,我会执行以下操作:

BindingSource bindingSource = dataGridView.DataSource as BindingSource;
List<Config> list = bindingSource.DataSource as List<Config>;
bindingSource.DataSource = list.Where(item => item.Server = 'banana').ToList();

如果您对 BindingSource(TextBoxes、ComboBoxes 等)有任何绑定,请记住 attaching/detaching DataSource 可能会导致不需要的行为。为了避免这种情况,我暂停和恢复绑定:

bindingSource.SuspendBinding();
// do the filtering
bindingSource.ResumeBinding();

这使所有数据绑定保持活动状态。

更新:仅显示 DataGridView 中的某些列

这很简单。 (列名称与数据源对象中的名称相关联)

var columnFunction = dataGridView.Columns["function"];
if(columnFunction != null)
    columnFunction.Visible = false;

var columnCheckType = dataGridView.Columns["checkType"];
if(columnCheckType != null)
    columnCheckType.Visible = false;

因此只会显示您的服务器列。