如何有效地填充 DataGridView 中生成的列?
How to populate a generated Column in a DataGridView efficiently?
我在我的 Winforms 程序中设置了 DataGridView,其中包含从数据库填充的信息。
我使用 Select * FROM [Data]
和以下代码填充了 DataGridView:
private void GetData(string selectCommand)
{
try
{
// Create a new data adapter based on the specified query.
dataAdapter = new OleDbDataAdapter(selectCommand, strConn);
// Create a command builder to generate SQL update, insert, and
// delete commands based on selectCommand.
OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter);
// Populate a new data table and bind it to the BindingSource.
DataTable table = new DataTable
{
Locale = CultureInfo.InvariantCulture
};
dataAdapter.Fill(table);
bindingSource1.DataSource = table;
// Resize the DataGridView columns to fit the newly loaded content.
LogDataGridView.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);
}
catch (OleDbException) { }
}
选项 1:
如果存储状态信息的数据库没有被其他进程不断更新,您可以简单地指定要加入的列是外部数据库中 table 的一部分。
如果它改为从其他来源接收更新,则您必须使用计时器对其进行轮询(或使用 FileSystemWatcher class 在数据库文件更改时接收通知)。不过,查询是相同的。
在JOIN子句后添加[;database=Second database Path].[Source Table] AS alias
,然后照常进行。
例如,设置一个DataGridView.Datasource指定第二个数据库路径。
LogDataGridView.DataSource = GetCrossJoinedTable([Second database path]);
连接字符串的 Data Source=
包含第一个数据库的路径。
然后,使用公共键和 return 包含来自第二个数据库中 Table 的状态信息的列([Order Status]
列,如果我正确阅读了更新的代码)。
private DataTable GetCrossJoinedTable(string secondDataBasePath)
{
var dt = new DataTable("JoinedTable");
string sql = "SELECT [Data].*, status.[Order Status] " +
$"FROM [Data] LEFT JOIN [;database={secondDataBasePath}].[Output] AS status " +
"ON [Data].[SO nr] = status.[Source No]";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=firstDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
using (var reader = cmd.ExecuteReader()) {
dt.Load(reader);
return dt;
}
}
}
选项 2:
查询第一个数据库,添加状态列,更新第二个数据库的状态信息,然后使用计时器轮询新的状态信息。
您可以调用LogDataGridView.DataSource = GetMainTableData()
设置数据源。它还将从第二个数据库加载状态信息。
Timer 将仅轮询第二个数据库,以检查 DataTable 中匹配的 [SO nr]
列的 [Order Status]
是否已更改。
当 DataTable(此处是名为 mainDT
的字段)更新时,您的 DataGraidView 将立即反映更改。
- 如果数据可以编辑,Timer 应该跳过 IsCurrentCellInEditMode returns
true
(read the notes there, about CheckBoxColumns), or paused while a Cell is in edit mode (CellBeginEdit event) and reumed when the Cell exits edit mode (CellEndEdit 事件的更新)。
- 记得在表单关闭时停止并处理 Timer(
Form.FormClosing
事件)。
如果您不需要计时器来轮询状态信息,只需在需要更新 DGV 时调用 UpdateStatusInfo()
。
System.Windows.Forms.Timer sqlTimer = null;
DataTable mainDT = new DataTable("MainTable");
private DataTable GetMainTableData()
{
string sql = "SELECT * FROM Data";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=firstDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
var reader = cmd.ExecuteReader();
mainDT.Load(reader);
mainDT.Columns.Add(new DataColumn() {
Caption = "Status", ColumnName = "fStatus", DataType = typeof(string), ReadOnly = true
});
}
sqlTimer = new System.Windows.Forms.Timer() { Interval = 5000 };
sqlTimer.Tick += (s, ev) => { UpdateStatusInfo(mainDT, false); };
sqlTimer.Start();
return UpdateStatusInfo(mainDT, true);
}
private DataTable UpdateStatusInfo(DataTable dt, bool returnTable)
{
string sql = "SELECT Output.[Order Status], Output.[Source No] FROM Output";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=secondDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
var reader = cmd.ExecuteReader();
dt.Columns["fStatus"].ReadOnly = false;
while (reader.Read()) {
dt.Select($"fNumber = {reader["Source No"]}").FirstOrDefault()?
.SetField("fStatus", reader["Order Status"].ToString());
}
dt.Columns["fStatus"].ReadOnly = true;
return returnTable ? dt : null;
}
}
你能做什么:
1/ 您从 OUTPUT table 创建一个 DataTable 变量(例如 OutputDataTable)
2/ 在您的方法 private void GetData(string selectCommand)
中,在设置数据源 (bindingSource1.DataSource = table;
) 之前,您订阅了一个事件 RowsAdded (dataGridView1.RowsAdded += this.DataGridView1_RowsAdded;
)。
如果您执行以下操作:
private void DataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
string valueToFind = dataGridView1[dataGridView1.Columns["SO nr"].Index, e.RowIndex].Value.ToString();
var row = OutputDataTable.Select($"Source No" = '{valueToFind}'").First();
string status = row["Order Status"].ToString();
dataGridView1[dataGridView1[dataGridView1.Columns["Status"].Index, e.RowIndex].Value = status;
}
我在我的 Winforms 程序中设置了 DataGridView,其中包含从数据库填充的信息。
我使用 Select * FROM [Data]
和以下代码填充了 DataGridView:
private void GetData(string selectCommand)
{
try
{
// Create a new data adapter based on the specified query.
dataAdapter = new OleDbDataAdapter(selectCommand, strConn);
// Create a command builder to generate SQL update, insert, and
// delete commands based on selectCommand.
OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter);
// Populate a new data table and bind it to the BindingSource.
DataTable table = new DataTable
{
Locale = CultureInfo.InvariantCulture
};
dataAdapter.Fill(table);
bindingSource1.DataSource = table;
// Resize the DataGridView columns to fit the newly loaded content.
LogDataGridView.AutoResizeColumns(
DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);
}
catch (OleDbException) { }
}
选项 1:
如果存储状态信息的数据库没有被其他进程不断更新,您可以简单地指定要加入的列是外部数据库中 table 的一部分。
如果它改为从其他来源接收更新,则您必须使用计时器对其进行轮询(或使用 FileSystemWatcher class 在数据库文件更改时接收通知)。不过,查询是相同的。
在JOIN子句后添加[;database=Second database Path].[Source Table] AS alias
,然后照常进行。
例如,设置一个DataGridView.Datasource指定第二个数据库路径。
LogDataGridView.DataSource = GetCrossJoinedTable([Second database path]);
连接字符串的 Data Source=
包含第一个数据库的路径。
然后,使用公共键和 return 包含来自第二个数据库中 Table 的状态信息的列([Order Status]
列,如果我正确阅读了更新的代码)。
private DataTable GetCrossJoinedTable(string secondDataBasePath)
{
var dt = new DataTable("JoinedTable");
string sql = "SELECT [Data].*, status.[Order Status] " +
$"FROM [Data] LEFT JOIN [;database={secondDataBasePath}].[Output] AS status " +
"ON [Data].[SO nr] = status.[Source No]";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=firstDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
using (var reader = cmd.ExecuteReader()) {
dt.Load(reader);
return dt;
}
}
}
选项 2:
查询第一个数据库,添加状态列,更新第二个数据库的状态信息,然后使用计时器轮询新的状态信息。
您可以调用LogDataGridView.DataSource = GetMainTableData()
设置数据源。它还将从第二个数据库加载状态信息。
Timer 将仅轮询第二个数据库,以检查 DataTable 中匹配的 [SO nr]
列的 [Order Status]
是否已更改。
当 DataTable(此处是名为 mainDT
的字段)更新时,您的 DataGraidView 将立即反映更改。
- 如果数据可以编辑,Timer 应该跳过 IsCurrentCellInEditMode returns
true
(read the notes there, about CheckBoxColumns), or paused while a Cell is in edit mode (CellBeginEdit event) and reumed when the Cell exits edit mode (CellEndEdit 事件的更新)。 - 记得在表单关闭时停止并处理 Timer(
Form.FormClosing
事件)。
如果您不需要计时器来轮询状态信息,只需在需要更新 DGV 时调用 UpdateStatusInfo()
。
System.Windows.Forms.Timer sqlTimer = null;
DataTable mainDT = new DataTable("MainTable");
private DataTable GetMainTableData()
{
string sql = "SELECT * FROM Data";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=firstDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
var reader = cmd.ExecuteReader();
mainDT.Load(reader);
mainDT.Columns.Add(new DataColumn() {
Caption = "Status", ColumnName = "fStatus", DataType = typeof(string), ReadOnly = true
});
}
sqlTimer = new System.Windows.Forms.Timer() { Interval = 5000 };
sqlTimer.Tick += (s, ev) => { UpdateStatusInfo(mainDT, false); };
sqlTimer.Start();
return UpdateStatusInfo(mainDT, true);
}
private DataTable UpdateStatusInfo(DataTable dt, bool returnTable)
{
string sql = "SELECT Output.[Order Status], Output.[Source No] FROM Output";
using (var conn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=secondDatabase.accdb;Persist Security Info=false;"))
using (var cmd = new OleDbCommand(sql, conn)) {
conn.Open();
var reader = cmd.ExecuteReader();
dt.Columns["fStatus"].ReadOnly = false;
while (reader.Read()) {
dt.Select($"fNumber = {reader["Source No"]}").FirstOrDefault()?
.SetField("fStatus", reader["Order Status"].ToString());
}
dt.Columns["fStatus"].ReadOnly = true;
return returnTable ? dt : null;
}
}
你能做什么:
1/ 您从 OUTPUT table 创建一个 DataTable 变量(例如 OutputDataTable)
2/ 在您的方法 private void GetData(string selectCommand)
中,在设置数据源 (bindingSource1.DataSource = table;
) 之前,您订阅了一个事件 RowsAdded (dataGridView1.RowsAdded += this.DataGridView1_RowsAdded;
)。
如果您执行以下操作:
private void DataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
string valueToFind = dataGridView1[dataGridView1.Columns["SO nr"].Index, e.RowIndex].Value.ToString();
var row = OutputDataTable.Select($"Source No" = '{valueToFind}'").First();
string status = row["Order Status"].ToString();
dataGridView1[dataGridView1[dataGridView1.Columns["Status"].Index, e.RowIndex].Value = status;
}