从多个线程中添加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();
}

从多个线程中添加DataRow到DataGridView

可以有多个线程,但是请只使用一个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() .