WPF (Windows Presentation Fundation)

Главная | Исходники | Книги | Ссылки |

Использование потоков и BackgroundWorker в WPF

BackgroundWoker предоставляет следующие основные методы:

RunWorkerAsync - служит для запуска потока.
DoWork - вызывается при старте потока. В этом методе обычно выполняется длительное вычисление, которое будет выполняться в отдельном потоке.
RunWorkerCompleted - вызывается по завершении выполнения потока и если поддерживается досрочное завершение при досрочном завершении.
ProgressChanged - может быть использован для отслеживания прогресса выполнения фонового потока.
CancelAsync - можно использовать для досрочного завершения фоновой операции. CancelAsync устанавливает соответствующее значение свойства CancellationPending. Фоновый код, поддерживающий досрочное завершение, проверяет значение этого свойства и, как только оно становится равным True, завершает свое выполнение.

Объявление BackgroundWorker в XAML коде.

Сначала нужно указать пространство имен System.ComponentModel, а дальше создать экземпляр BackgroundWorker в разделе ресурсов окна.

  1.   <Window.Resources>
  2.     <my:BackgroundWorker
  3.       x:Key="backgroundWoker"
  4.       WorkerReportsProgress="True"
  5.       WorkerSupportsCancellation="True"
  6.       DoWork="BackgroundWorker_DoWork"
  7.       ProgressChanged="BackgroundWorker_ProgressChanged"
  8.       RunWorkerCompleted="BackgroundWorker_RunWorkerCompleted"
  9.       >
  10.     </my:BackgroundWorker>
  11.   </Window.Resources>
* This source code was highlighted with Source Code Highlighter.

Теперь для получения доступа к ресурсу в коде прописываем в кострукторе окна и не забываем про uses System.ComponentModel:

  1.   public partial class MainWindow : Window
  2.   {
  3.     private BackgroundWorker backgroundWorker;
  4.     public MainWindow()
  5.     {
  6.       InitializeComponent();
  7.       backgroundWorker = (BackgroundWorker)this.FindResource("backgroundWoker");
  8.     }
  9.   ...
* This source code was highlighted with Source Code Highlighter.

Отслеживание прогресса

Мы установили BackgroundWoker.WokerReportsProgress в True для возможности отслеживания прогресса в выполнении. В коде события DoWork мы должны вызвать метод ReportProgress() и указать процент готовности. При каждом вызове ReportProgress() объект BackgroundWoker инициирует событие ProgressChanged. Это событие считывает процент готовности и обновляет интерфейс. Важно, что событие ProgressChanged инициировано в потоке пользовательского интерфейса, поэтому никаких дополнительных действий предпринимать не надо.

Поддержка отмены

Для поддержки отмены добавим кнопку "Отмена" и также мы уже указали явно в XAML коде WorkerSupportsCancellation="True" . При нажатии на кнопку в событии Click вызываем метод BackgroundWorker.CancelAsync(), теперь в коде DoWork при проверке if (worker.CancellationPending == true) мы просто возвращаем управление ничего не делая. Важно, что даже при отмене генерируется событие RunWokerCompleted, поэтому это нужно предусмотреть и обработать соотвественно.

Дополнения

В код, полный текст которого приведен ниже, добавлено изменение видимости ProgressBar в зависимости от логики и доступность кнопок "Старт" и "Отмена". Т.е. пока процесс не запушен кнопка "Отмена" не доступна, когда процесс запущен, то доступна кнопка "Отмена", а кнопка "Старт" становиться не доступна.

Вот, полный текст XAML файла

  1. <Window x:Class="BWTest.MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:my="clr-namespace:System.ComponentModel;assembly=System"
  5.     Title="BackgroundWorker тест" Height="150" Width="250">
  6.   <Window.Resources>
  7.     <my:BackgroundWorker
  8.       x:Key="backgroundWoker"
  9.       WorkerReportsProgress="True"
  10.       WorkerSupportsCancellation="True"
  11.       DoWork="BackgroundWorker_DoWork"
  12.       ProgressChanged="BackgroundWorker_ProgressChanged"
  13.       RunWorkerCompleted="BackgroundWorker_RunWorkerCompleted"
  14.       >
  15.     </my:BackgroundWorker>
  16.   </Window.Resources>
  17.   <Grid>
  18.     <Grid.RowDefinitions>
  19.       <RowDefinition Height="100*" MinHeight="70"></RowDefinition>
  20.       <RowDefinition Height="40*" MinHeight="40"></RowDefinition>
  21.     </Grid.RowDefinitions>
  22.       <Button Content="Старт" Height="23" HorizontalAlignment="Left" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
  23.       <Button Content="Отмена" Height="23" HorizontalAlignment="Left" Margin="81,0,0,0" Name="btnCancel" VerticalAlignment="Top" Width="75" Click="btnCancel_Click" IsEnabled="False"/>
  24.     <DockPanel Grid.Row="1" Background="Beige" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
  25.       <StatusBar DockPanel.Dock="Left" Height="40" MinHeight="30" HorizontalAlignment="Left" VerticalAlignment="Center" Name="statusBar" Background="Beige">
  26.         <StatusBarItem>
  27.           <ProgressBar Height="10" MinHeight="10" Name="progressBar" Width="100" Visibility="Hidden" />
  28.         </StatusBarItem>
  29.         <StatusBarItem MinHeight="30">
  30.           <Label Height="30" Name="txtProgress" HorizontalAlignment="Left" VerticalAlignment="Center" VerticalContentAlignment="Center" Width="100" />
  31.         </StatusBarItem>
  32.       </StatusBar>
  33.     </DockPanel>
  34.   </Grid>
  35. </Window>
* This source code was highlighted with Source Code Highlighter.

И полный текст кода  на C#

  1. using System.Windows;
  2. using System.ComponentModel;
  3.  
  4. namespace BWTest
  5. {
  6.  
  7.   public partial class MainWindow : Window
  8.   {
  9.     private BackgroundWorker backgroundWorker;
  10.     public MainWindow()
  11.     {
  12.       InitializeComponent();
  13.       backgroundWorker = (BackgroundWorker)this.FindResource("backgroundWoker");
  14.     }
  15.  
  16.     private void BackgroundWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
  17.     {
  18.       txtProgress.Content = (e.ProgressPercentage.ToString() + "%");
  19.       progressBar.Value = e.ProgressPercentage;
  20.     }
  21.  
  22.     private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
  23.     {
  24.       BackgroundWorker worker = sender as BackgroundWorker;
  25.       for (int i = 1; (i <= 10); i++)
  26.       {
  27.         if ((worker.CancellationPending == true))
  28.         {
  29.           e.Cancel = true;
  30.           break;
  31.         }
  32.         else
  33.         {
  34.           System.Threading.Thread.Sleep(500);
  35.           worker.ReportProgress((i * 10));
  36.         }
  37.       }
  38.     }
  39.  
  40.     private void BackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
  41.     {
  42.       if ((e.Cancelled == true))
  43.       {
  44.         this.progressBar.Visibility = Visibility.Hidden;
  45.         this.txtProgress.Content = "Отмена!";
  46.       }
  47.  
  48.       else if (!(e.Error == null))
  49.       {
  50.         this.progressBar.Visibility = Visibility.Hidden;
  51.         this.txtProgress.Content = ("Ошибка: " + e.Error.Message);
  52.       }
  53.  
  54.       else
  55.       {
  56.         this.txtProgress.Content = "Готово!";
  57.         this.progressBar.Visibility = Visibility.Hidden;
  58.         btnCancel.IsEnabled = false;
  59.         btnStart.IsEnabled =true;
  60.       }
  61.  
  62.     }
  63.  
  64.     private void btnStart_Click(object sender, RoutedEventArgs e)
  65.     {
  66.       btnStart.IsEnabled = false;
  67.       btnCancel.IsEnabled = true;
  68.       progressBar.Value = 0;
  69.       progressBar.Visibility = Visibility.Visible;
  70.       backgroundWorker.RunWorkerAsync();
  71.  
  72.     }
  73.  
  74.     private void btnCancel_Click(object sender, RoutedEventArgs e)
  75.     {
  76.       if (backgroundWorker.WorkerSupportsCancellation == true)
  77.       {
  78.         btnStart.IsEnabled = true;
  79.         backgroundWorker.CancelAsync();
  80.         btnCancel.IsEnabled = false;
  81.       }
  82.     }
  83.   }
  84. }
* This source code was highlighted with Source Code Highlighter.

Ссылки по теме:

WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI

Создание более быстро реагирующих приложений с Dispatcher

Joseph Albahari. Работа с потоками в C#. Часть 1. Часть 2.

Threading Model

 


Главная | Программирование | Покер | Фотография | Разное
© Андрей Семёнов, 2009
Hosted by uCoz