从几个线程向DataGridView添加DataRow

时间:2014-05-12 15:26:44

标签: c# multithreading winforms datagridview

我遇到了一个我不知道如何解决的问题。

我创建了一个包含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();
}

1 个答案:

答案 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()