从另一个线程访问MainWindow控件的最佳方法是什么?

时间:2016-05-17 08:45:49

标签: c# multithreading

我构建了一个工作得很好的同步工具,但是噪音问题是我用来冻结用户界面的重要方法。
现在,我知道我可以使用Thread或Task解决这种情况,但在Sync();方法中,我有很多访问UI控件的权限。

实际上,为了访问MainWindow控件,我创建了这样的东西:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}

所以我可以从任何课程中做到这一点:MainWindow.AppWindow.UIControlName。可以想象,如果我使用Task或Thread,这个解决方案将变得毫无用处。因为我需要为每个控件写这个:

MainWindow.AppWindow.UIControlName.Dispatcher.BeginInvoke(
      (Action)(() => { 
           MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
      }));

想象一下,我有20个或更多这些行,代码会变得非常丑陋且长,以便于访问UI控件。

现在我想知道什么是异步调用方法并在不阻塞UI的情况下立即返回调用者的最佳方法。

如果没有针对此类应用程序的异步编程,用户体验并不是真正的写作。

1 个答案:

答案 0 :(得分:3)

您可以使用调度程序将所有丑陋的东西包装在不同的方法中并使用它们,或者像这样使用async \ await:

private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}

LongRunningOperation在哪里:

public async Task<SomeResult> LongRunningOperation() {
    // do some stuff here
    return new SomeResult();
}

public async Task LongRunningOperation() {
    // do some stuff
}

你也可以这样做:

 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}

这是因为默认情况下,在等待捕获当前同步上下文之前,以及等待此上下文恢复之后。在UI应用程序(如WPF或WinForms)中,这意味着如果在等待你在UI线程之前 - 你也会在等待之后,因此可以自由访问UI控件。

您可能会问 - 何时应该声明您的方法是异步的,何时只使用Task.Run。如果您进行纯CPU计算 - 可以使用Task.Run。如果您执行任何IO(文件访问,网络访问等) - 声明您的方法异步并使用IO相关类提供的异步方法(如FileStream.ReadAsync等)。

例如:

private async Task LongRunningOperation() {
    string contents;
    using (var fs = File.OpenRead("some file")) {
        using (var reader = new StreamReader(fs)) {
            contents = await reader.ReadToEndAsync();
        }
    }
    var downloadedFile = await new WebClient().DownloadDataTaskAsync("some file url");
}