我遇到了一个我不知道如何解决的问题。
我创建了一个包含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)大部分都没有显示。
任何想法如何解决这个问题?
以下是我可以查看的代码中的一些代码:
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 - 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();
}
答案 0 :(得分:1)
可以有多个线程,但请只使用一个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()
。