0 votes

Généraliser l'interface d'une barre de progression dans une interface graphique

Je travaille sur une application qui a un formulaire principal avec une barre d'état en bas. La barre d'état contient une barre de progression (ProgressBar) et une étiquette (Label) que je veux utiliser pour montrer à l'utilisateur la progression actuelle d'un travail en cours. La barre d'état contient également une étiquette que je souhaite utiliser comme un bouton d'annulation cliquable.

J'ai déjà créé des interfaces asynchrones comme celle-ci, mais elles étaient toujours basées sur une seule action, et en utilisant un travailleur en arrière-plan. Mais dans ce nouveau programme, il y a un certain nombre d'actions différentes que l'utilisateur peut déclencher et pour lesquelles je veux utiliser la même barre d'état pour montrer la progression. J'essaie donc de trouver un moyen de généraliser et de standardiser l'interface de ces contrôles de rapport de progression dans la barre d'état.

Dans certains cas, les processus asynchrones sont créés à l'aide d'un BackGroundWorker, mais dans d'autres cas, je dois créer et gérer les threads secondaires directement.

Voici un code squelette à moitié terminé de ce que j'ai pensé :

public partial class MyForm: System.Windows.Forms.Form
{
    ...SNIP...

    private void WorkProgressChanged(Object sender, EventArgs args)
    {
        ProgressChangedEventArgs backgroundWorkerArgs = args as ProgressChangedEventArgs;
        ProgressReport formReport = args as ProgressReport; //my own custom progress report class

       //tries to cast args to a Background worker args
        if (backgroundWorkerArgs != null)
        {
            // update UI based on backgroundWorkerArgs

        }
        else if (formReport != null)
        {
            // update UI basd on formReport

        }

        else 
        {
            //couldn't figure out what kind of progress report was sent
            //update UI based on args.ToString();

        }

    }

    private void cancelButtonToolStripLabel_Click(object sender, EventArgs e)
    {
        //calls cancel method of current processing
        if (this._currentWorkCancelAction != null)
        {
            _currentWorkCancelAction(); //envoke cancel requet
            cancelButtonToolStripLabel.Text = "Canceling"; //shows user that cancel request was made
            _currentWorkCancelAction = null; //disaccociates cancel button to prevent user from canceling twice
        }
    }

    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;

    }

....SNIP

}

La barre d'état est donc mise à jour en souscrivant à l'événement `WorkProgressChanged(Object sender, EventArgs args) ', et finalement réinitialisée lorsque 'WorkProcessCompleted(Object sender, EventArgs args)' est invoqué par un événement d'achèvement. Mon étiquette d'annulation (bouton) doit également être associée puis dissociée d'une méthode déléguée qui demandera l'annulation du travail en cours.

Ainsi, chaque fois qu'un travail est effectué, un certain nombre de choses doivent se produire. Les abonnements aux événements sont ajoutés/supprimés, les références des délégués sont modifiées, etc. etc. Je me suis donc demandé s'il n'y avait pas un moyen d'encapsuler toutes ces actions dans une ou deux méthodes réutilisables plutôt que d'écrire du code en double pour chaque action qui peut avoir lieu.

Les InitWorkProcess() La méthode ci-dessous montre comment je pense que cela peut fonctionner. Bien que je sois presque sûr que ce n'est pas la bonne façon d'utiliser la classe EventDescriptor, je n'ai pas trouvé d'autre moyen de référencer un événement en tant que paramètre de méthode. Je n'ai pas trouvé d'autre moyen de référencer un événement en tant que paramètre d'une méthode. Peut-être n'est-ce pas possible ?

    public void InitWorkProcess(EventDescriptor workProgressChangedEvent, EventDescriptor workCompletedEvent, System.Action requestCancel)
    { 
        //subscribe to progress changed
        workProgressChangedEvent.AddEventHandler(this, this.WorkProgressChanged);
        this._workProgressChangedEvent = workProgressChangedEvent;

        //subscribe to process completed
        workCompletedEvent.AddEventHandler(this, this.WorkProcessCompleted);
        this._workCompletedEvent = workCompletedEvent;

        //enable cancel button
        if (requestCancel != null)
        {
            cancelButtonToolStripLabel.Visible = true;
            this._currentWorkCancelAction = requestCancel;
        }
    }

... et je modifierais la méthode de gestion de l'événement WorkProgressComplete pour désabonner les relations d'événement lorsque le travail est terminé.

    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;

        //unsubscribes WorkProcessCompleted() and WorkProgressChanged() methods
        this._workCompletedEvent.RemoveEventHandler(this, this._workCompletedEvent);
        this._workCompletedEvent = null;

        this._workProgressChangedEvent.RemoveEventHandler(this, this._workProgressChangedEvent);
        this._workProgressChangedEvent = null;
    }

Quelqu'un a-t-il des suggestions sur la manière de procéder ? Devrais-je simplement oublier le InitWorkProcess() et ajouter/supprimer toutes les relations événement/délégué séparément pour chaque action ? Ou existe-t-il une meilleure méthode ?

1voto

Jodrell Points 14205

Je ne pense pas que la barre de progression doive être directement abonnée aux événements.

Il devrait mettre en œuvre une interface qui expose toutes les fonctionnalités pertinentes de la barre de progression, ce qui permettrait de gérer les appels inter-filières et de rendre le contrôle plus sûr. Le code interne doit ignorer les types externes, c'est-à-dire qu'il doit être encapsulé.

Le "câblage" devrait se trouver dans le parent commun des contrôles, en associant les événements aux modifications de la barre de progression, par l'intermédiaire de l'interface publique.

0voto

jrista Points 20950

Pourquoi ne pas utiliser ProgressChangedEventArgs à partir de votre thread personnalisé également, et capitaliser sur un objet d'événement commun à partir de n'importe quel travailleur en arrière-plan... plutôt que d'essayer de gérer plusieurs types d'objets de rapport de progression ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X