2

似たような質問がたくさんあることを知っており、それらの多くを読みました。残念ながら、それらを読んでもまだ問題を解決できませんでしたが、私はC#に比較的慣れていません。ドキュメントによるとInvalidOperationException、デバッガーを使用すると、スレッドセーフではないという問題が発生します。これは私の問題には当てはまりません。

問題に集中するために、単純な生のテスト クラスで問題を再現しました。

メイン フォームは、一種の進行状況ダイアログを表示することになっています。

public partial class ImportStatusDialog : Form
{
    public ImportStatusDialog()
    {
        InitializeComponent();
    }

    public void updateFileStatus(string path)
    {
        t_filename.Text = path;         
    }

    public void updatePrintStatus()
    {      
        t_printed.Text = "sent to printer";
    }

    public void updateImportStatus(string clientName)
    {
        t_client.Text = clientName;
    }

    public void updateArchiveStatus()
    {
        t_archived.Text = "archived";
    }
}

そのコードがメイン フォームから Invoke() なしで呼び出された場合:

private void button1_Click(object sender, EventArgs e)
    {
        ImportStatusDialog nDialog = new ImportStatusDialog();

        nDialog.Show();

        nDialog.updateFileStatus("test");
        Thread.Sleep(1000);
        nDialog.updateImportStatus("TestClient");
        Thread.Sleep(1000);
        nDialog.updatePrintStatus();
        Thread.Sleep(1000);
        nDialog.updateArchiveStatus();
        Thread.Sleep(1000);

        nDialog.Close();
    }

そして、私がこのように呼んでも:

private void button3_Click(object sender, EventArgs e)
    {
        ImportStatusDialog nDialog = new ImportStatusDialog();

        nDialog.Show();

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateFileStatus("Test");
            });
        }
        else
        {
            nDialog.updateFileStatus("Test");
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updatePrintStatus();
            });
        }
        else
        {
            nDialog.updatePrintStatus();
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateImportStatus("cName");
            });
        }
        else
        {
            nDialog.updateImportStatus("cName");
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateArchiveStatus();
            });
        }
        else
        {
            nDialog.updateArchiveStatus();
        }

        Thread.Sleep(1000);

        nDialog.Close();
    }

デザイナーでこのように見えるダイアログ(私の例では)

デザイナー ビュー

次のように表示されます。

実行時のダイアログ

Show( ) の代わりにShowDialog()を使用すると、ダイアログは正しく表示されますが、API Doc が指摘するように

このメソッドを使用して、アプリケーションでモーダル ダイアログ ボックスを表示できます。このメソッドが呼び出されると、その後のコードはダイアログ ボックスが閉じられるまで実行されません。

特にダイアログの更新は、ダイアログが再び閉じられた後にのみ行われることを意味するため、これは私が望むものではありません。

ここで何が間違っていますか?これは些細な問題のようですが、解決策は私を回避します。私は C# GUI プログラミングが初めてであることを覚えておいてください。

また、Invoke() を使用するのに適切な場所はどこでしょうか? ダイアログメソッドを呼び出すときにメインフォームで使用しますか、それともダイアログ更新メソッド自体で使用しますか?

4

1 に答える 1

2

ここでは複数のスレッドを使用しているようには見えないため、呼び出すものは必要ありません。Invoke はクロススレッド呼び出しにのみ必要です。複数のフォームを作成していますが、すべてのコードが同じスレッドで実行されています。

メイン UI スレッドで作業を行っている場合 (コードがボタン クリック イベントに含まれていることからもわかるように)、Application.DoEvents()定期的に呼び出して進行状況フォームを更新できるようにします。

より良い解決策はBackgroundWorker、作業に を使用し、その進行状況を定期的に報告させることです。

次に、BackgroundWorker のProgressChangedイベントで進行状況フォームを更新できます (これはメイン スレッドで実行されるため、何も呼び出す必要はありません)。

于 2012-08-28T22:11:38.557 に答える