W ramach kontynuacji tematu rozpoczętego we wpisie „70-511: Enhancing Usability: Implementacja przetwarzania asynchronicznego (teoria)”, zapraszam do zapoznania się z przykładem kodu źródłowego, który będzie ilustracją do przedstawionej teorii (przykład kodu źródłowego dotyczy implementacji przetwarzania asynchronicznego, ze szczególnym naciskiem na to, jak jest to rozwiązane w WPF).W ramach przykładu zostanie pokazane wykorzystanie BacgroundWorker'a i Dispatcher'a.
W przykładzie zostanie wykorzystane proste okno z dwoma przyciskami (uruchamiającym i anulującym operację do wykonania w tle), paskiem postępu i etykietą pokazującą stan operacji. W kodzie XAML można to zapisać np. tak:
<Window x:Class="AsynchronousProcessing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="Button_GO" Click="Button_GO_Click">Go</Button>
<Button Content="Cancel" Height="23" Name="button_Cancel" Click="button_Cancel_Click" />
<ProgressBar Height="20" Name="progressBar1"/>
<Label >Result:</Label>
<Label Content="Label" Height="28" Name="label_result" />
</StackPanel>
</Window>
W klasie okna powinniśmy zadeklarować obiekt BackgroundWorker'a,
np.:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
który inicjujemy w konstruktorze okna:
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler( worker_RunWorkerCompleted );
worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler( worker_ProgressChanged );
worker.DoWork += new System.ComponentModel.DoWorkEventHandler( worker_DoWork );
Przycisk Go będzie uruchamiał operację w tle
(worker.RunWorkerAsync();).
Przycisk Cancel będzie anulował operację (worker.CancelAsync();).
Pasek postępu będzie aktualizowany jako obsługa zdarzenia
ProgressChanged (progressBar1.Value
= e.ProgressPercentage;). Po zakończeniu operacji w tle
będziemy w etykiecie związane z rezultatem wpisywać rezultat lub
informację, że operacja była anulowana:
void worker_RunWorkerCompleted( object sender, System.ComponentModel.RunWorkerCompletedEventArgs e )
{
if ( !e.Cancelled )
label_result.Content = e.Result;
else
label_result.Content = "canceled";
}
Po wykonaniu powyższej konfiguracji, czas na zapisanie kodu operacji w tle. W tym przypadku będzie to prosta pętla od 0 do 100, w której
będzie sprawdzany warunek anulowania, raportowany będzie postęp
oraz wykonywana będzie pauza na 100 ms:
void worker_DoWork( object sender, System.ComponentModel.DoWorkEventArgs e )
{
for ( int i = 0; i < 100; i++ )
{
if ( worker.CancellationPending )
{
e.Cancel = true;
break;
}
worker.ReportProgress( i );
System.Threading.Thread.Sleep( 100 );
}
e.Result = "Done";
}
A co w przypadku, gdy chcielibyśmy w etykiecie rezultatu wypisywać
procent zaawansowania operacji? Można by było wykorzystać
zdarzenie ProgressChanged (i jest to zalecane podejście!),
ale można również ręcznie aktualizować etykietę bezpośrednio z
wątku BackgroundWorker'a, trzeba tylko wykorzystać
Disparcher'a. Można to zapisać w sposób następujący:
delegate void methodewithIntargument( int i );
private void UpdateLabel( int i )
{
if ( label_result.Dispatcher.CheckAccess() )
label_result.Content = i;
else
label_result.Dispatcher.BeginInvoke( new methodewithIntargument( UpdateLabel ), new object[] { i } );
}
Ostatecznie kod całej klasy w tym przykładzie wygląda następująco:
public partial class MainWindow: Window
{
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
delegate void methodewithIntargument( int i );
public MainWindow()
{
InitializeComponent();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler( worker_RunWorkerCompleted );
worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler( worker_ProgressChanged );
worker.DoWork += new System.ComponentModel.DoWorkEventHandler( worker_DoWork );
}
void worker_DoWork( object sender, System.ComponentModel.DoWorkEventArgs e )
{
for ( int i = 0; i < 100; i++ )
{
if ( worker.CancellationPending )
{
e.Cancel = true;
break;
}
UpdateLabel( i );
worker.ReportProgress( i );
System.Threading.Thread.Sleep( 100 );
}
e.Result = "Done";
}
private void UpdateLabel( int i )
{
if ( label_result.Dispatcher.CheckAccess() )
label_result.Content = i;
else
label_result.Dispatcher.BeginInvoke( new methodewithIntargument( UpdateLabel ), new object[] { i } );
}
void worker_ProgressChanged( object sender, System.ComponentModel.ProgressChangedEventArgs e )
{
progressBar1.Value = e.ProgressPercentage;
}
void worker_RunWorkerCompleted( object sender, System.ComponentModel.RunWorkerCompletedEventArgs e )
{
if ( !e.Cancelled )
label_result.Content = e.Result;
else
label_result.Content = "canceled";
}
private void Button_GO_Click( object sender, RoutedEventArgs e )
{
worker.RunWorkerAsync();
}
private void button_Cancel_Click( object sender, RoutedEventArgs e )
{
worker.CancelAsync();
}
}











Maciej_Zbrzezny on delicious.com
Brak komentarzy:
Prześlij komentarz