2016-02-04 2 views
0

Я хочу обновления статуса из долгого метода. Обычно я использую диспетчер, чтобы отправить обратно в поток пользовательского интерфейса, но мне интересно, как использовать async.async WPF несколько обновлений статуса в потоке пользовательского интерфейса

Для простоты:

Создать окно, добавьте кнопку

<Button Name="ButtonWithCodeBehind" Height="25" Click="ButtonWithCodeBehindOnClick"/> 

добавить некоторый код позади обработчика OnClick

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e) 
{ 
    await Task.Factory.StartNew(() => 
    { 
     ButtonWithCodeBehind.Content = "First"; 
     Thread.Sleep(1000); 
     ButtonWithCodeBehind.Content = "Second"; 
     Thread.Sleep(1000); 
     ButtonWithCodeBehind.Content = "Third"; 
    }); 
} 

Это, очевидно, будет перерыв, потому что ButtonWithCodeBehind.Content будет доступ к неправильной теме.

Есть ли способ, чтобы сделать эту работу, не делая что-то вроде:

Deployment.Current.Dispatcher.BeginInvoke(()=>ButtonWithCodeBehind.Content = "Second"); 

Критическим моментом здесь является длинный ход задача будет генерировать обновления, как она прогрессирует, я могу реорганизовать код что-то вроде этого :

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e) 
{ 
    var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
    await Task.Factory.StartNew(() => 
    { 
     Task.Factory.StartNew(() => Thread.Sleep(1000)) 
      .ContinueWith(t => ButtonWithCodeBehind.Content = "First", scheduler) 
      .ContinueWith(t => Thread.Sleep(1000)) 
      .ContinueWith(t => ButtonWithCodeBehind.Content = "Second", scheduler) 
      .ContinueWith(t => Thread.Sleep(1000)) 
      .ContinueWith(t => ButtonWithCodeBehind.Content = "Third", scheduler); 
    }); 
} 

Но это изящно. Кроме того, если вы вытащили асинхронные и ожидали ключевые слова и заменили их на Task.WaitAll, он все равно выполнил бы, как ожидалось.

Примечание: Если вам интересно, почему я использую Thread.Sleep вместо Task.Delay, я на самом деле тестирую это в Silverlight, а поддержка async-ожидания не включает .Delay (или по крайней мере не там, где я ожидаю, что это будет).

ответ

2

Если вы можете разделить свою давно выполняющуюся задачу на два разных длительных действия (например, два Thread.Sleeps в вашем примере выше), вы можете подождать каждую долговременную задачу самостоятельно. Таким образом, обновления пользовательского интерфейса будут выполняться в потоке пользовательского интерфейса.

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e) 
{ 
    ButtonWithCodeBehind.Content = "First"; 
    await Task.Run(() => Thread.Sleep(1000)); 
    ButtonWithCodeBehind.Content = "Second"; 
    await Task.Run(() => Thread.Sleep(1000)); 
    ButtonWithCodeBehind.Content = "Third"; 
} 
+0

для имитации работы Task.Run + Thread.Sleep в порядке. для «ожидания немного» Task.Delay - это способ пойти, хотя – Dbl

+0

Спасибо Андреасу, Thread.Sleep был потому, что я использовал Silverlight в то время, а Silverlight не включает Task.Delay :( – Ian

1

Единственная часть, которую нужно ожидать, это долговременная часть - вызов IO или, в данном случае, связанный с процессором сон.

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e) 
{ 
    ButtonWithCodeBehind.Content = "First"; 
    await Task.Factory.StartNew(() => Thread.Sleep()); 
    ButtonWithCodeBehind.Content = "Second"; 
    await Task.Factory.StartNew(() => Thread.Sleep()); 
    ButtonWithCodeBehind.Content = "Third"; 
} 

Await захватывает контекст синхронизации и гарантирует, что остальная часть метода подписался на продолжение, которое выполняется в потоке с тем же контекстом. В WPF поток пользовательского интерфейса обрабатывает код ButtonWithCodeBehindOnClick и, таким образом, по умолчанию будет отвечать за остальную часть вызова метода после await.

Вы можете переопределить это поведение по умолчанию, настроив AWAIT на задаче:

await Task.Factory.StartNew(() => 
    Thread.Sleep()).ConfigureAwait(continueOnCapturedContext: false); 

Однако, вы совершенно не хотите сделать это в WPF как Threadpool нить будет пытаться обновить пользовательский интерфейс.

Смежные вопросы