0

asyncアプリをレスポンシブにするためにawaitキーワードを使用しています

Viewプロパティがで発生した場合、次のコードがありますViewModel

表示:ボタンクリックイベント

    private async void button1_Click(object sender, RoutedEventArgs e)
    {
        button1.IsEnabled = false;

        // Property Change
        _viewModel.Words = await Task.Factory.StartNew(() => File.ReadAllLines("Words.txt").ToList()); // takes time to read about 3 - 4 seconds

        switch (_viewModel.RadioButtonWordOrderSelection)
        {
            case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
                break;

            case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
                await Task.Factory.StartNew(() =>
                                                {
                                                    var words = _viewModel.Words.ToList();
                                                    words.Reverse();
                                                    // Property Change
                                                    _viewModel.Words = words;
                                                });
                break;

            case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
                await Task.Factory.StartNew(() =>
                                                {
                                                    // Property Change
                                                    _viewModel.Words = _viewModel.Words.Shuffle().ToList();
                                                });
                break;
        }

        await Task.Factory.StartNew(() => DownloadSomething(_viewModel.Words)); // takes time to read about 30 - 40 seconds

        button1.IsEnabled = true;
    }

_viewModel.Progressで更新されますView

ビュー:プライベートメソッドの完了には30〜40秒かかります

    private void DownloadSomething(IEnumerable<string> words)
    {
        // Property Change
        _viewModel.Progress = 0;

        foreach (var word in words)
        {
            // Property Change
            _viewModel.Word = word;
            try
            {
                // Some code WebClient download code here
            }
            catch (Exception e)
            {
                //Trace.WriteLine(e.Message);
            }

            // Property Change
            _viewModel.Progress++;
        }
    }

ビュー:処理されたプロパティ変更イベント

    void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        try
        {
            switch(e.PropertyName)
            {
                case "Progress":
                    // Since its a different Thread
                    // http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
                    // Sets the Value on a ProgressBar Control.
                    // This will work as its using the dispatcher

                    // The following works
                    //Dispatcher.Invoke(
                    //    DispatcherPriority.Normal, 
                    //    new Action<double>(SetProgressValue), 
                    //    _viewModel.Progress);

                    // from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
                    progress1.Dispatcher.Invoke(
                        DispatcherPriority.Normal, 
                        new Action(() =>
                        {
                            progress1.Value = _viewModel.Progress;
                        })
                    );

                    // This will throw an exception 
                    // (it's on the wrong thread)
                    //progress1.Value = _viewModel.Progress;
                    break;

                case "Words":
                    // Since its a different Thread
                    // http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
                    // Sets the Max Value on a ProgressBar Control.
                    // This will work as its using the dispatcher

                    // The following Works
                    //Dispatcher.Invoke(
                    //    DispatcherPriority.Normal,
                    //    new Action<double>(SetProgressMaxValue),
                    //    _viewModel.Words.Count);

                    // from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
                    progress1.Dispatcher.Invoke(
                        DispatcherPriority.Normal, 
                        new Action(() =>
                        {
                            progress1.Maximum = _viewModel.Words.Count;
                        })
                    );

                    // This will throw an exception 
                    // (it's on the wrong thread)
                    //progress1.Maximum = _viewModel.Words.Count;
                    break;

                case "Word":
                    // Since its a different Thread
                    // http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
                    // Sets the Contant on a Label Control.
                    // This will work as its using the dispatcher

                    // The following Works
                    //Dispatcher.Invoke(
                    //    DispatcherPriority.Normal,
                    //    new Action<string>(SetLastWordValue),
                    //    _viewModel.Word);

                    // from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
                    labelLastWord.Dispatcher.Invoke(
                        DispatcherPriority.Normal, 
                        new Action(() =>
                        {
                            labelLastWord.Content = _viewModel.Word;
                        })
                    );

                    // This will throw an exception 
                    // (it's on the wrong thread)
                    //labelLastWord.Content = _viewModel.Word;
                    break;

                case "RadioButtonWordOrderSelection":
                    break;

                default:
                    throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
        }
    }

UIはprogressBar1とlabelLastWordに対して完全に更新されます!ただし、progressBar1とlabelLastWordが更新されているときに、UIの残りの部分がフリーズするという問題に直面しています。

これを修正するための回避策はありますか?

私はどんな助けにも大いに感謝します!

4

1 に答える 1

1

タスクベースの非同期プログラミングドキュメントのガイドラインに従うことを強くお勧めします。これは、を介してスレッドプールに作業をシャントするよりもはるかに優れていますStartNew。CPUにバインドされた操作がある場合は、を使用できますがTask.Runそれ以外の場合は、既存のasync対応エンドポイントを使用します。

また、VM全体をUIコンテキストにあるものとして扱うと便利だと思います。したがってPropertyChanged、常にUIコンテキストで発生します。特にデータバインディングはこれに依存します。

private async Task<List<string>> ReadAllLinesAsync(string file)
{
  var ret = new List<string>();
  using (var reader = new StreamReader(file))
  {
    string str = await reader.ReadLineAsync();
    while (str != null)
    {
      ret.Add(str);
      str = await reader.ReadLineAsync();
    }
  }
  return ret;
}

private async void button1_Click(object sender, RoutedEventArgs e)
{
  button1.IsEnabled = false;

  _viewModel.Words = await ReadAllLinesAsync("Words.txt");

  List<string> words;
  switch (_viewModel.RadioButtonWordOrderSelection)
  {
    case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
      break;

    case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
      await Task.Run(() =>
      {
        words = _viewModel.Words.ToList();
        words.Reverse();
      });
      _viewModel.Words = words;
      break;

    case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
      await Task.Run(() =>
      {
        words = _viewModel.Words.Shuffle().ToList();
      });
      _viewModel.Words = words;
      break;
  }

  await DownloadSomething(_viewModel.Words);

  button1.IsEnabled = true;
}

private async Task DownloadSomething(IEnumerable<string> words)
{
  _viewModel.Progress = 0;

  foreach (var word in words)
  {
    _viewModel.Word = word;
    try
    {
      await ...; // async WebClient/HttpClient code here
    }
    catch (Exception e)
    {
      //Trace.WriteLine(e.Message);
    }

    _viewModel.Progress++;
  }
}

void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
  try
  {
    switch(e.PropertyName)
    {
      case "Progress":
        progress1.Value = _viewModel.Progress;
        break;

      case "Words":
        progress1.Maximum = _viewModel.Words.Count;
        break;

      case "Word":
        labelLastWord.Content = _viewModel.Word;
        break;

      case "RadioButtonWordOrderSelection":
        break;

      default:
        throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
    }
  }
  catch(Exception ex)
  {
    MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
  }
}

最後に、 JoshSmithのMVVM本を購入してよく読むことをお勧めします。「View」や「ViewModel」などの用語を使用していますが、これらのコンポーネントの使用方法では、MVVMパターンのすべての利点を完全に回避しています。

于 2012-11-01T23:03:13.483 に答える