0

これは、別のフォームからダイアログを更新するためのフォローアップの質問です (コードとスクリーンショットはそこにあります)。

GUI がハングする問題を解決するために、次の 2 つの推奨事項を受け取りました。

  • 使用するApplication.DoEvents()

  • を使ってBackgroundWorker

DoEvents() アプローチは機能しますが、使用すべきではないことが指摘されています。実際、GUI は正しく更新されますが、短時間応答しません。

だからこそ、私は BackgroundWorker を使いたいと思っていて、それを読んでいます。

ただし、サンプルコードの4つのラベルを個別に更新するために使用できるように実装する方法がわかりません。プログラムが 1 つのジョブを正常に終了したときに、進行状況を表示 (および 4 つのダイアログ ラベルを更新) したいと考えています。ただし、 BackgroundWorker には 1 つしかDoWork()ありません。の を使用してe.ArgumentDoWorkEventArgsさまざまな更新方法を区別しようとしましたが、失敗しました。

public partial class BackgroundWorkerImportStatusDialog : Form
{
    private BackgroundWorker dialogWorker = new BackgroundWorker();

    private string path;
    private string clientName;

    public BackgroundWorkerImportStatusDialog()
    {
        InitializeComponent();
    }

    public void updateFileStatus(string path)
    {
        this.path = path;

        dialogWorker = new BackgroundWorker();
        dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
        dialogWorker.RunWorkerAsync(UpdateComponent.FileStatus);

    }

    public void updatePrintStatus()
    {
        dialogWorker = new BackgroundWorker();
        dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
        dialogWorker.RunWorkerAsync(UpdateComponent.PrintStatus);
    }

    public void updateImportStatus(string clientName)
    {
        this.clientName = clientName;

        dialogWorker = new BackgroundWorker();
        dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
        dialogWorker.RunWorkerAsync(UpdateComponent.ImportStatus);
    }

    public void updateArchiveStatus()
    {
        dialogWorker = new BackgroundWorker();
        dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
        dialogWorker.RunWorkerAsync(UpdateComponent.ArchiveStatus);
    }

    private void updateLabels(object sender, DoWorkEventArgs e)
    {
        MessageBox.Show(e.Argument.ToString());
        if ((UpdateComponent) e.Argument == UpdateComponent.FileStatus)
        {
            t_filename.Text = path;
        }
        if ((UpdateComponent) e.Argument == UpdateComponent.PrintStatus)
        {
            t_printed.Text = "sent to printer";
        }
        if ((UpdateComponent) e.Argument == UpdateComponent.ImportStatus)
        {
            t_client.Text = clientName;
        }
        if ((UpdateComponent) e.Argument == UpdateComponent.ArchiveStatus)
        {
            t_archived.Text = "archived";
        }
    }

    public enum UpdateComponent { FileStatus, PrintStatus, ImportStatus, ArchiveStatus}

そして、この非常に些細なダイアログに 4 つの BackgroundWorker を用意することが解決策になるとは想像できません。

4

1 に答える 1

1

あなたの質問を理解しているので、実行中のアプリケーションの 4 つの側面について、ダイアログ フォームでユーザーに通知する必要があります。

  • 印刷状況
  • ファイルの状態
  • 輸入状況
  • アーカイバのステータス

バックグラウンドワーカーを使用して、それぞれを定期的にチェックできます。各操作のステータスがチェックされた後、プログレスバーを 25% 進めることができます (そして適切な情報で UI を更新します)。

非同期プログラミングを試すこともできます。つまり、操作を開始するだけで、アプリケーションを続行できます。操作が完了すると、アプリケーションに通知され、フォームの情報が更新される場合があります。使用している .NET フレームワークに応じて、async( await.NET 4.5 / C# 5 以降で利用可能 - MSDN で async & await ) または非同期プログラミングへの従来のアプローチを使用できます。


編集:

この状況で BackgroundWorker が最適なソリューションであるかどうかはわかりません。次のようなものがあると想像できます:

  1. BackhgroundWorker は 1 回だけチェックします。つまり、印刷ステータスを 1 回、ファイル ステータスを 1 回、インポート ステータスを 1 回、アーカイバ ステータスを 1 回チェックします。これはばかげているように聞こえるかもしれませんが、ユーザーの動作を促進する可能性があります。つまり、ユーザーがこのメカニズムをクリックするか、他の方法でこのメカニズムを呼び出すと、明示的に起動されます。ProgressBar をアプリケーションのステータスバーに配置して、ユーザーが「アプリケーションが実際に何かを実行している」ことを認識できるようにします。

  2. 以前のアプローチは少し改善される可能性があります-BackgroundWorkerで実際にジョブを完了することはありません-代わりに、メインメソッド内で無限ループが発生します。これにより、定期的に物事を確認することができます。このアプローチでは、進捗を増やしても意味がありません。

2 番目のアプローチのサンプル:

private void bg_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    for (int i = 1; i <= 10; i++)
    {
        if (worker.CancellationPending == true)
        {
            e.Cancel = true;
            break;
        }
        else
        {
            CheckPrintingStatus();
            CheckFileStatus();
            CheckImportStatus();
            CheckArchiverStatus();
            System.Threading.Thread.Sleep(5000); // sleep for 5 seconds
        }
    }
}

この解決策 (2 番目のアプローチ) が、スレッドを明示的に作成するよりも優れているかどうかという疑問があります。4 つの異なるスレッドを作成して、それぞれが別のことをチェックできるようにすることを考えることができます。これは OS にとっては少し重くなりますが、一方で、操作ごとに異なるスリープ時間を設定できます。

ベア スレッドを使用する場合は、スレッドを明示的に作成する代わりにThreadPoolを使用することをお勧めします。

于 2012-09-02T13:04:22.837 に答える