Barre de progression WPF et multithreading

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>

Écran de la barre de progression
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 Dispatcherobjets 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:
DispatcherPriorityUtilisé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 BackgroundWorkertant 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 DoWorket BackgroundWorkerl'é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 = Truelorsque 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 RunWorkerCompletedtravail, 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' BackgroundWorkerobjet 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.

DoworkL'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 Doworkcas, vous pouvez appeler BackgroundWorkerune 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

DoWorkUne 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 RunWorkerCompletedEventArgsparamè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

BackgroundWorkerLa 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的CancellationPendingattribut pour Truemarquer le thread comme annulé.

Le développeur doit DoWorkjuger l' CancellationPendingattribut 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.DoWorkEventArgsCancelTrueCancellationPending

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 ~

Je suppose que tu aimes

Origine blog.csdn.net/qq_42068856/article/details/109015792
conseillé
Classement