0

我正在使用asyncawait关键字来使应用程序响应

ViewViewModel.

VIEW:按钮点击事件

    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.ProgressView

视图:私有方法需要 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++;
        }
    }

VIEW:已处理的属性更改事件

    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);
        }
    }

为 progressBar1 和 labelLastWord 完美更新了 UI!我遇到了一个问题,当progressBar1 和labelLastWord 正在更新UI 的其余部分时被冻结。

有没有办法解决这个问题?

我非常感谢任何帮助!

4

1 回答 1

1

我强烈建议您遵循基于任务的异步编程文档中的指南。这比仅仅通过将工作分流到线程池要好得多StartNew。如果您确实有 CPU 密集型操作,则可以使用Task.Run,但对于其他所有操作,请使用现有async的 -ready 端点。

此外,我发现将整个 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);
  }
}

最后,我建议您购买并通读Josh Smith 的 MVVM 书。您正在使用诸如“视图”和“视图模型”之类的术语,但是您使用这些组件的方式,您完全避免了 MVVM 模式的所有优点。

于 2012-11-01T23:03:13.483 回答