web-dev-qa-db-ja.com

WinForms ProgressBarでの非同期/待機

私は過去にこのタイプのことをBackgroundWorkerで機能させましたが、.NET 4.5の新しい非同期/待機アプローチを使用したいと思います。間違った木を吠えているかもしれません。お知らせ下さい。

目標:長時間実行される作業を行うコンポーネントを作成し、作業を行っているときに進行状況バーでモーダルフォームを表示します。コンポーネントはウィンドウへのハンドルを取得して、長時間実行されている作業の実行中に相互作用をブロックします。

Status:以下のコードを参照してください。窓とのやり取りをするまでは、うまくやっていると思いました。そのままにしておくと(つまり、触らないでください!)、すべてが「完全に」実行されますが、どちらかのウィンドウをクリックするほど、長時間の作業が終了した後にプログラムがハングします。 UIスレッドがブロックされているかのように、実際の相互作用(ドラッグ)は無視されます。

質問:コードをかなり簡単に修正できますか?もしそうなら、どのように?または、別のアプローチ(BackgroundWorkerなど)を使用する必要がありますか?

コード(Form1は、ProgressBarと、ProgressBarの値を設定するパブリックメソッドUpdateProgressを備えた標準フォームです):

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Starting..");
        var mgr = new Manager();
        mgr.GoAsync();
        Console.WriteLine("..Ended");
        Console.ReadKey();
    }
}

class Manager
{
    private static Form1 _progressForm;

    public async void GoAsync()
    {
        var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
        _progressForm = new Form1();
        _progressForm.Show(owner);

        await Go();

        _progressForm.Hide();
    }

    private async Task<bool> Go()
    {
        var job = new LongJob();
        job.OnProgress += job_OnProgress;
        job.Spin();
        return true;
    }

    void job_OnProgress(int percent)
    {
        _progressForm.UpdateProgress(percent);
    }
}

class LongJob
{
    public event Progressed OnProgress;
    public delegate void Progressed(int percent);

    public void Spin()
    {
        for (var i = 1; i <= 100; i++)
        {
            Thread.Sleep(25);
            if (OnProgress != null)
            {
                OnProgress(i);
            }
        }
    }
}

class Win32Window : IWin32Window
{
    private readonly IntPtr _hwnd;
    public Win32Window(IntPtr handle)
    {
        _hwnd = handle;
    }
    public IntPtr Handle
    {
        get
        {
            return _hwnd;
        }
    }
}
}
17
Todd Sprang

@StephenClearyの答えは正しいです。しかし、OPが望む動作を得るには、彼の答えを少し変更する必要がありました。

public void GoAsync() //no longer async as it blocks on Appication.Run
{
    var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
    _progressForm = new Form1();

    var progress = new Progress<int>(value => _progressForm.UpdateProgress(value));

    _progressForm.Activated += async (sender, args) =>
        {
            await Go(progress);
            _progressForm.Close();
        };

    Application.Run(_progressForm);
}
7
YK1

asyncおよびawaitキーワードは、「バックグラウンドスレッドで実行する」という意味ではありません。私は async/awaitブログのイントロ を持っていますdo平均。 Task.Runのように、CPUにバインドされた操作をバックグラウンドスレッドに明示的に配置する必要があります。

また、 タスクベースの非同期パターン のドキュメントでは、asyncコードを使用した一般的なアプローチ(進行状況レポートなど)について説明しています。

class Manager
{
  private static Form1 _progressForm;

  public async Task GoAsync()
  {
    var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
    _progressForm = new Form1();
    _progressForm.Show(owner);

    var progress = new Progress<int>(value => _progressForm.UpdateProgress(value));
    await Go(progress);

    _progressForm.Hide();
  }

  private Task<bool> Go(IProgress<int> progress)
  {
    return Task.Run(() =>
    {
      var job = new LongJob();
      job.Spin(progress);
      return true;
    });
  }
}

class LongJob
{
  public void Spin(IProgress<int> progress)
  {
    for (var i = 1; i <= 100; i++)
    {
      Thread.Sleep(25);
      if (progress != null)
      {
        progress.Report(i);
      }
    }
  }
}

Progress<T>タイプはスレッドマーシャリングを適切に処理するため、Form1.UpdateProgress内でマーシャリングを行う必要はありません。

18
Stephen Cleary
private async void button1_Click(object sender, EventArgs e)
{
    IProgress<int> progress = new Progress<int>(value => { progressBar1.Value = value; });
    await Task.Run(() =>
    {
        for (int i = 0; i <= 100; i++)
            progress.Report(i);
    });
}

私が間違っている場合は修正してください。ただし、これが進行状況バーを更新する最も簡単な方法のようです。

4
user2539367