【发布时间】:2014-06-30 01:15:48
【问题描述】:
我遇到了一个我不知道如何解决的问题。
我创建了一个包含 3 个表单的应用程序。 frm1 是完成所有繁重工作的主要形式。 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)大多都没有显示。
有什么办法解决这个问题吗?
以下是我的代码中的一些 sn-ps,您可以看看:
frm1 - 主要
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 - 设置
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 - 日志窗口
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();
}
【问题讨论】:
-
我第一次听说
ApartmentState,但是.NET Framework 不使用套间,托管对象自己负责以线程安全的方式使用所有共享资源。你确定你知道你在做什么?另一件事是您需要在执行Invoke/BeginInvoke之前检查控制IsHandleCreated。 -
好吧,我是 Winforms 的新手。主要只开发 dll 和控制台应用程序。即使我从普通线程运行它:它仍然是一样的
-
不。然后就变得不一样了,因为你必须在 UI 线程中进行 UI 操作(而且你必须只有一个 UI 线程)。您可以尝试使用 host(大多数父表单)来处理尚未创建(无处理)表单的调用。
-
所以我不能将数据传递到 logviews 线程上的网格,对吧?我不确定如何访问主 UI 线程。
标签: c# multithreading winforms datagridview