从多个线程中添加DataRow到DataGridView
本文关键字:DataRow DataGridView 添加 线程 | 更新日期: 2023-09-27 18:03:00
我遇到了一个我不知道如何解决的问题。
我创建了一个包含3个表单的应用程序。fr1是所有繁重工作都要完成的主要形式。Frm2只是一个设置表单。Frm3是一个状态窗口,可能会显示从frm1和frm2接收到的一些日志。在启动时,两个表单(frm2和frm3)将被初始化,但不显示。要显示其中一个表单,您需要单独打开它(通知图标)。
每个表单(frm2和frm3)都作为STA线程运行,因为主表单的负载很重,有时会导致表单显示巨大的延迟。
当我运行frm3(包含一个DataGridView)没有首先启动设置,一切都很好。Frm3继续记录frm2和frm1所需的所有信息。即使我只运行frm2,它也会一直登录到DataGridView。但是首先启动frm2(它开始从frm3登录到DataGridView),然后运行frm3导致InvalidOperationException,因为frm2持有从frm3访问DataGridView的权限。我真的不能通过Invoke传递数据,因为这两个表单(frm2和frm3)大多没有显示。
有什么办法解决这个问题吗?
下面是我的一些代码片段,你可以看看:
frm1 - Main
public partial class Tray : Form
{
private Settings s = new Settings();
public static LogWindow log = new LogWindow();
public Tray()
{
InitializeComponent();
Data.ni_tray = ni_tray;
ctm.MenuItems.Add("Start", StartStop);
ctm.MenuItems.Add("Einstellungen", OpenSettings);
ctm.MenuItems.Add("Log", OpenLog);
ctm.MenuItems.Add("Exit", CloseApp);
ni_tray.ContextMenu = ctm;
}
private void OpenSettings(object sender, EventArgs e)
{
if (!s.Visible)
{
Thread thread = new Thread(() => s.ShowDialog());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
else
s.Invoke(new MethodInvoker(() => { s.locate(); }));
}
private void OpenLog(object sender, EventArgs e)
{
if (!log.Visible)
{
Thread thread = new Thread(() => log.ShowDialog());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
else
log.Invoke(new MethodInvoker(() => { log.locate(); }));
}
[ ... }
}
frm2 - Settings
public partial class Settings : Form
{
private readonly string[] info = { String.Empty, "Füge Programm hinzu ...", "Füge Szenario hinzu ...", "Sichere Konfiguration ...", "Übernehme Änderungen ohne Sicherung ...", "Verwerfe Konfiguration ...", "Konfiguration erfolgreich geladen ...", "Konfiguration erfolgreich gespeichert ..." };
[ ... ]
private void WriteConf()
{
writeSection(conf.Root.Element("Applications"), dgv_apps);
writeSection(conf.Root.Element("Scenarios"), dgv_scen);
writeSection(conf.Root.Element("Misc"));
conf.Save(ConfFile);
Tray.log.writeLogEntry("Settings", info[7]);
showInfo(info[7]);
}
[ ... ]
}
frm3 - LogWindow
public partial class LogWindow : Form
{
public LogWindow()
{
InitializeComponent();
}
private void btn_close_Click(object sender, EventArgs e)
{
this.Hide();
}
private void dgv_log_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
btn_clear.Enabled = true;
dgv_log.ClearSelection();
dgv_log.FirstDisplayedScrollingRowIndex = dgv_log.Rows.Count - 1;
dgv_log.Rows[dgv_log.Rows.Count - 1].Selected = true;
}
public void writeLogEntry(string application, string info)
{
if (dgv_log.InvokeRequired)
this.Invoke((MethodInvoker)delegate { dgv_log.Rows.Add(DateTime.Now.ToShortTimeString(), application, info); });
else
dgv_log.Rows.Add(DateTime.Now.ToShortTimeString(), application, info);
}
private void btn_clear_Click(object sender, EventArgs e)
{
dgv_log.Rows.Clear();
btn_clear.Enabled = false;
}
}
这个将导致异常:
if (!log.Visible)
{
Thread thread = new Thread(() => log.ShowDialog());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
可以有多个线程,但是请只使用一个UI线程。有一个主窗口,所以所有的窗口都可以使用它的实例来调用。
基本上:
public partial class FormTray : Form
{
private static _instance;
public static Instance { get { return _instance; } } // to get from anywhere
public FormTray()
{
InitializeComponents();
_instance = this; // store instance
}
private void OpenSettings(object sender, EventArgs e)
{
FormSettings.Show(); // call static method
}
private void OpenLog(object sender, EventArgs e)
{
FormLog.Show(); // call static method
}
// ...
}
public partial class FormSettings
{
private static FormSettings _instance; // to be used from static methods
public FormSettings()
{
InitializeComponents();
_instance = this;
}
public static Show()
{
if(_instance == null) // not yet created - create and show
{
_instance = new FormSettings();
_instance.Show();
}
else
_instance.Visible = true; // was created and hidden - un-hide
}
void FormSettings_Closing(object sender, FormClosingEventArgs e)
{
e.Cancel = true; // disable closing
Visible = false; // hide instead
}
// ...
}
public partial class FormLog
{
// ... same as settings
// static method for log messages
public static AddMessage(string message)
{
if(FormTray.Instance != null && FormTray.Instance.IsHandleCreated) // avoid errors if attempting to log before main form is created
{
if(FormTray.Instance.InvokeRequired)
FormTray.Instance.BeginInvoke(() = { AddMessage(message); } // need invoke
else
{
// ... logging here
}
}
}
}
这不是一个完整的解决方案,只是一些想法。
- 命名。
- 使用
Visible
=true/false代替打开/关闭表单。 - 每个表单的单例排序。
- 通过主表单实例调用日志消息。
- 线程安全
FormLog.AddMessage()
.