WPFプログレスバーとマルチスレッド
この記事では、進行状況バーコントロールを例として使用して、WPFマルチスレッドを紹介します。進行状況バーの例により、WPFマルチスレッドの特性を包括的に理解できます。
ユーザーが何かをダウンロードしたり、アプリケーションに大量のデータを読み込んだりすると、必然的に長時間待つ必要があります。このとき、プログラムがクラッシュしているとユーザーが思わないように、進行状況をリアルタイムでユーザーに反映する進行状況バーが必要です。一連のハニージュース操作。
それでは、最初にプログレスバーに行きましょう。
XAMLコード:
<Grid Margin="100,0">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<ProgressBar
Name="ProgressBar"
Grid.Row="0"
Width="580"
Height="30"
Maximum="100"
Minimum="0" />
<DockPanel Grid.Row="1" LastChildFill="False">
<Button
Width="100"
Height="30"
Click="Download_OnClick"
Content="Download"
DockPanel.Dock="Left" />
</DockPanel>
</Grid>
プログレスバーの進行状況を制御する方法はたくさんありますが、この記事では主に4つの代表的な方法を紹介します。WPFでマルチスレッドを使用する方法を紹介するために使用されます。
1つのシングルスレッド(失敗)
背景コード:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Download按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Download_OnClick(object sender, RoutedEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
ProgressBar.Value = i;
Thread.Sleep(100);
}
}
}
このメソッドは、UIスレッドの下で実行され、メソッドの実行中にUIスレッドがスタックし、メソッドが終了したときにのみ画面に反映されるため、明らかに進行状況バーの実装に失敗します。
したがって、進行状況バーが0から100に突然変化し、中間プロセスは表示されません。
次に、タスククラスマルチスレッドを使用して進行状況バーを実装します(失敗)
UIスレッドを使用すると、プログラムがフリーズします。誰もが簡単に考えて、スレッドコントロールを使用できます。これはUIスレッドには影響しません。
そうです、現時点ではマルチスレッドしか使用できないので、次のコードがあります。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Download按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Download_OnClick(object sender, RoutedEventArgs e)
{
Task task = new Task(TaskMethod);
task.Start();
}
private void TaskMethod()
{
for (int i = 1; i <= 100; i++)
{
ProgressBar.Value = i;
Thread.Sleep(50);
}
}
}
新しいスレッドには進行状況バーがないため、上記のコードを使用すると、間違いなく異常に実行され、プログラムが終了します。スレッドで見つからないオブジェクトを強制的に呼び出すと、エラーが発生します。したがって、タスクスレッドでアプリケーションオブジェクトにアクセスしようとしないでください。
3つ目は、Dispatcherを使用してスレッドを実行する
スレッド内のアプリケーションオブジェクトを操作Dispatcher
するには、現在のアプリケーションのオブジェクトを使用できます。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Download按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Download_OnClick(object sender, RoutedEventArgs e)
{
Task task = new Task(TaskMethod);
task.Start();
}
private void TaskMethod()
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
Dispatcher.BeginInvoke((ThreadStart)delegate
{
ProgressBar.Value = i;
}, DispatcherPriority.Normal);
}
}
}
Dispatcherが現在のプログラムを取得した後、BeginInvoke(DispatcherPriority, Delegate)
非同期メソッドを使用して、非同期メソッドはデリゲートを呼び出して、呼び出し元のアプリケーションオブジェクトを他のスレッドに実装します。
このメソッドには2つのパラメータがあります。:
DispatcherPriority
スレッドの優先度を示すために使用されます。通常はあまり使用されません。優先度の高い方が最初に実行されます。
Delegate
:委任します。BeginInvoke()メソッドは、着信メソッドをスケジューラのタスクとしてスケジュールします。その後、スケジューラーはこのメソッドを実行します。
最後に、上記の3つの方法は弱いです。実際、BackgroundWorkerコンポーネントを使用すると、進行状況バーの機能とほぼ完全に一致する可能性があります。
4、BackgroundWorkerを使用して進行状況バーを達成します
使いやすくBackgroundWorker
するために、ウィンドウにリソースとして配置できますResources
。
属性
1.WorkerReportsProgress
このプロパティがTrueに設定されている場合、進行状況の更新メソッドがトリガーされます。
2.WorkerSupportsCancellation
このプロパティがTrueに設定されている場合、cancelメソッドがトリガーされます。
イベント
1.DoWork
非同期DoWork
で実行する必要のあるメソッドを追加するとBackgroundWorker
、作業時にイベントがトリガーされます。
2.ProgressChanged
Addメソッドは、進行状況が更新されたとき、進行状況が更新されProgressChanged
たWorkerReportsProgress = True
ときにイベントがトリガーされたときに実行されます。
3.RunWorkerCompleted
RunWorkerCompleted
作業の最後にフィードバックメソッドを追加すると、作業の最後にイベントがトリガーされます。
方法
1.RunWorkerAsync(オブジェクト引数)
object argument
:非同期スレッドで使用できるようにオブジェクトを渡します。
非同期操作を開始します。
2.ProgressChanged(intpercentProgress)
int percentProgress
:現在の進捗状況を示す値を渡します
トリガーRunWorkerCompleted
イベント
<Window
x:Class="ThreadDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=System"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ThreadDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="300"
mc:Ignorable="d">
<Window.Resources>
<componentModel:BackgroundWorker
x:Name="Worker"
x:Key="Worker"
DoWork="Worker_OnDoWork"
ProgressChanged="Worker_OnProgressChanged"
RunWorkerCompleted="Worker_OnRunWorkerCompleted"
WorkerReportsProgress="True"
WorkerSupportsCancellation="True" />
</Window.Resources>
<Grid Margin="100,0">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<ProgressBar
Name="ProgressBar"
Grid.Row="0"
Width="580"
Height="30"
Maximum="100"
Minimum="0" />
<DockPanel Grid.Row="1" LastChildFill="False">
<Button
Width="100"
Height="30"
Click="Download_OnClick"
Content="Download"
DockPanel.Dock="Left" />
<Button
Width="100"
Height="30"
Click="Cancel_OnClick"
Content="Cancel"
DockPanel.Dock="Right" />
</DockPanel>
</Grid>
</Window>
public partial class MainWindow : Window
{
private BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
worker = (BackgroundWorker)FindResource("Worker");
}
/// <summary>
/// Download按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Download_OnClick(object sender, RoutedEventArgs e)
{
worker?.RunWorkerAsync(ProgressBar);
}
/// <summary>
/// Cancel按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Cancel_OnClick(object sender, RoutedEventArgs e)
{
worker?.CancelAsync();
}
/// <summary>
/// 线程工作方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Worker_OnDoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
worker.ReportProgress(i);
Thread.Sleep(100);
}
}
/// <summary>
/// 进度改变方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Worker_OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressBar.Value = e.ProgressPercentage;
}
/// <summary>
/// 工作完成方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Worker_OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("已取消");
return;
}
MessageBox.Show("下载完成");
}
}
コードに示されているように、
1.BackgroundWorkerオブジェクトを初期化します
この例では、コンストラクターでBackgroundWorker
オブジェクトを初期化します。
2.非同期操作の実行を開始します
[ダウンロード]ボタンのクリックイベントで、BackgroundWorker的RunWorkerAsync(object argument)
メソッドを使用して非同期スレッドを実行します。
注:Doworkで追加されたメソッドは、非同期スレッドで実行されます。したがって、Doworkイベントでアプリケーションオブジェクトを呼び出すことはできないことに注意してください。
Dowork
イベントで呼び出す必要のあるオブジェクトは、パラメーターとして渡すことができます。
3.フォームへのリアルタイムフィードバックの進行状況
この方法はDowork
いつでも呼び出すことができにおけるイベントフォームに進捗状況をフィードバックします。メソッドが呼び出されるとメソッドがトリガーされ、ウィンドウがリアルタイムで更新されて進行状況がユーザーに報告されます。メソッドはUIスレッドにあるため、アプリケーションオブジェクトを任意に呼び出すことができます。BackgroundWorker
ReportProgress(int percentProgress)
Worker_OnProgressChanged()
4.メソッドの最終結果のフィードバック
DoWork
イベントの実行がRunWorkerCompleted
終了すると、スレッドの実行が終了したことを示すイベントがトリガーされます。この方法では、RunWorkerCompletedEventArgs
パラメータを使用して、スレッドの実行ステータスが正常に終了したかキャンセルされたかなどを判断し、フォームの更新方法を決定できます。
5.スレッドをキャンセルします
法と呼ばれる任意の時点でスレッドを終了することができます。スレッドの終わりですが、実際には自動的に終了しませんが、スレッドをキャンセル済みとしてマークするように属性を設定します。BackgroundWorker
CancelAsync()
BackgroundWorker的CancellationPending
True
開発者は、DoWork
メソッドのCancellationPending
属性を判断して、スレッドを終了するかどうかを決定する必要があります。キャンセル状態が設定された後もRunWorkerCompleted
イベントはトリガーされることに注意してください。状態をイベントに渡す
ために設定する必要DoWorkEventArgs
のあるCancel
プロパティ、そして最後にendメソッドで、プロパティが結果をフォームにフィードバックする方法を決定したと判断されます。True
CancellationPending
上記の進行状況バーは、BackgroundWorkerを使用して簡単に実現できます。もちろん、これに加えて、このクラスを使用して、非同期メソッドを実装し、進行状況をフィードバックし、いつでもキャンセルすることができます。
それはあなたを助けますか?いいね〜