Barre de progression WPF et multithreading
Cet article utilisera un contrôle de barre de progression comme exemple pour présenter le multi-threading WPF. L'exemple de la barre de progression peut nous donner une compréhension complète des caractéristiques du multi-threading WPF.
Lorsqu'un utilisateur télécharge quelque chose ou charge une grande quantité de données dans notre application, l'utilisateur devra inévitablement attendre longtemps. À ce stade, nous avons besoin d'une barre de progression pour refléter la progression à l'utilisateur en temps réel, afin d'éviter que l'utilisateur pense que le programme plante. Une série d'opérations de jus de miel.
Alors, passons d'abord à une barre de progression.
Code 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>
Il existe de nombreuses façons de contrôler la progression de la barre de progression, mais cet article présente principalement 4 méthodes représentatives . Utilisé pour présenter comment utiliser le multithreading dans WPF.
Un seul thread (échec)
Code de fond:
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);
}
}
}
Cette méthode ne parvient évidemment pas à implémenter la barre de progression, car elle est exécutée sous le thread d'interface utilisateur et le thread d'interface utilisateur est bloqué lorsque la méthode est en cours d'exécution, et elle ne sera reflétée à l'écran que lorsque la méthode est terminée.
Par conséquent, vous verrez la barre de progression changer soudainement de 0 à 100 et vous ne pourrez pas voir le processus intermédiaire.
Deuxièmement, utilisez le multithreading de classe de tâche pour implémenter la barre de progression (échec)
L'utilisation du thread d'interface utilisateur va geler le programme. Il est facile pour tout le monde de réfléchir, puis d'utiliser le contrôle des threads. Cela n'affectera pas le thread de l'interface utilisateur.
C'est vrai, vous ne pouvez utiliser que le multi-thread pour le moment, il existe donc le code suivant.
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);
}
}
}
L'utilisation du code ci-dessus fonctionnera sans aucun doute anormalement et entraînera la fin du programme, car il n'y a pas de barre de progression dans le nouveau thread et l'appel forcé d'un objet qui ne peut pas être trouvé dans le thread provoquera une erreur. Par conséquent, n'essayez pas d'accéder aux objets d'application dans le thread de tâche.
Troisièmement, utilisez Dispatcher pour exécuter des threads
Pour manipuler des objets d'application dans un thread, vous pouvez utiliser les Dispatcher
objets de l' application actuelle .
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);
}
}
}
Après que Dispatcher Obtient le programme actuel, à l'aide de la BeginInvoke(DispatcherPriority, Delegate)
méthode asynchrone, la méthode asynchrone appelle le délégué pour implémenter les objets d'application appelants dans d'autres threads.
Cette méthode a deux paramètres:
DispatcherPriority
Utilisée pour indiquer la priorité du thread, généralement peu utilisée, la priorité la plus élevée est exécutée en premier.
Delegate
: Délégué, la méthode BeginInvoke () planifiera la méthode entrante comme la tâche du planificateur. Le planificateur exécutera alors cette méthode.
Enfin, les trois méthodes ci-dessus sont faibles. En fait, l'utilisation du composant BackgroundWorker peut correspondre presque parfaitement à la fonction de la barre de progression.
Quatre, utilisez BackgroundWorker pour atteindre la barre de progression
Pour faciliter l'utilisation, vous pouvez le mettre en BackgroundWorker
tant que ressource dans la fenêtre Resources
.
Les attributs
1.WorkerReportsProgress
Lorsque cette propriété est définie sur True, la méthode de mise à jour de la progression est déclenchée.
2.WorkerSupportsAnnulation
Lorsque cette propriété est définie sur True, la méthode d'annulation est déclenchée.
un événement
1.DoWork
Ajoutez la méthode qui doit être exécutée de manière asynchrone DoWork
et BackgroundWorker
l'événement sera déclenché lorsque vous travaillez.
2.ProgressChanged
Add la méthode est exécutée lorsque la progression est mise à jour dans ProgressChanged
, WorkerReportsProgress = True
lorsque l'événement est déclenché lors d'une mise à jour de progression.
3.RunWorkerCompleted
Ajoutez la méthode de rétroaction à la fin du RunWorkerCompleted
travail, et l'événement sera déclenché à la fin du travail.
méthode
1.RunWorkerAsync (argument d'objet)
object argument
: Passez un objet pour qu'il puisse être utilisé dans des threads asynchrones.
Démarrez l'opération asynchrone.
2.ProgressChanged (int percentProgress)
int percentProgress
: Passez une valeur pour indiquer la progression actuelle
RunWorkerCompleted
Événement déclencheur
<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("下载完成");
}
}
Comme indiqué dans le code,
1. Initialisez l'objet BackgroundWorker
Dans cet exemple, nous initialisons l' BackgroundWorker
objet dans le constructeur .
2. Commencez à effectuer des opérations asynchrones
Dans l'événement de clic sur le bouton Télécharger, BackgroundWorker的RunWorkerAsync(object argument)
exécutez des threads asynchrones à l'aide de méthodes.
Remarque: les méthodes ajoutées dans Dowork seront exécutées dans des threads asynchrones. Par conséquent, n'oubliez pas que l'objet d'application ne peut pas être appelé dans l'événement Dowork.
Dowork
L'objet qui doit être appelé dans l' événement peut être transmis en tant que paramètre.
3. Progression des commentaires en temps réel sur le formulaire
Dans ce Dowork
cas, vous pouvez appeler BackgroundWorker
une ReportProgress(int percentProgress)
méthode à tout moment pour remonter la progression du formulaire.
La méthode est déclenchée lorsque cette méthode est appelée Worker_OnProgressChanged()
et la fenêtre est mise à jour en temps réel pour signaler la progression à l'utilisateur. Étant donné que cette méthode se trouve dans le thread d'interface utilisateur, l'objet d'application peut être appelé de manière arbitraire.
4. Commentaires sur les résultats finaux de la méthode
DoWork
Une fois l'exécution de l'événement terminée, un RunWorkerCompleted
événement sera déclenché pour indiquer la fin de l'exécution du thread. Dans cette méthode, vous pouvez utiliser des RunWorkerCompletedEventArgs
paramètres pour déterminer si l'état d'exécution du thread est normalement terminé ou annulé, etc., pour décider comment mettre à jour le formulaire.
5. Annuler le fil
BackgroundWorker
La CancelAsync()
méthode appelée peut terminer le thread à tout moment.
Bien que ce soit la fin du thread, mais en fait, il ne se terminera pas automatiquement, mais définissez l' BackgroundWorker的CancellationPending
attribut pour True
marquer le thread comme annulé.
Le développeur doit DoWork
juger l' CancellationPending
attribut dans la méthode pour décider s'il faut mettre fin au thread. Il convient de noter que l' RunWorkerCompleted
événement sera toujours déclenché après l'avoir défini sur l'état annulé. La propriété qui
doit être définie pour transmettre l'état à l' événement, et enfin dans la méthode end, il est estimé que la propriété a décidé comment renvoyer le résultat au formulaire.DoWorkEventArgs
Cancel
True
CancellationPending
Ci-dessus, la barre de progression peut être facilement réalisée en utilisant BackgroundWorker. Bien sûr, en plus de cela, vous pouvez utiliser cette classe pour implémenter n'importe quelle méthode asynchrone, remonter la progression et annuler à tout moment.
Cela vous aide-t-il? J'aime ça ~