2010-08-27 5 views
4

Configuration: formulaire MDI principal avec une barre de progression et une étiquette.Winforms Mise à jour du modèle asynchrone de l'interface utilisateur - Nécessité de généraliser

Code dans la forme principale.

public delegate void UpdateMainProgressDelegate(string message, bool isProgressBarStopped); 

      private void UpdateMainProgress(string message, bool isProgressBarStopped) 
      { 
       // make sure we are running on the right thread to be 
       // updating this form's controls. 
       if (InvokeRequired == false) 
       { 
        // we are running on the right thread. Have your way with me! 
        bsStatusMessage.Caption = message + " [ " + System.DateTime.Now.ToShortTimeString() + " ]"; 
        progressBarStatus.Stopped = isProgressBarStopped; 
       } 
       else 
       { 
        // we are running on the wrong thread. 
        // Transfer control to the correct thread!     
        Invoke(new ApplicationLevelValues.UpdateMainProgressDelegate(UpdateMainProgress), message, isProgressBarStopped); 
       } 
      } 

formulaire enfant

private readonly ApplicationLevelValues.UpdateMainProgressDelegate _UpdateMainForm; 
private void btnX_Click(object sender, EventArgs e) 
     { 
      _UpdateMainForm.BeginInvoke("StartA", false, null, null); 
      try 
      { 
       if(UpdateOperationA()) 
       { _UpdateMainForm.BeginInvoke("CompletedA", true, null, null); } 
       else 
       { _UpdateMainForm.BeginInvoke("CanceledA", true, null, null); } 
      } 
      catch (System.Exception ex) 
      { 
       _UpdateMainForm.BeginInvoke("ErrorA", true, null, null); 
       throw ex; 
      } 
     } 

Son travail assez bien, mais pour les boutons N ou N opérations que je dois écrire le même code encore et encore. Est-il possible de généraliser ou de mettre en place une autre façon de mettre à jour l'interface utilisateur?

Répondre

4

Ceci est vraiment juste un simple problème de refactoring si vos opérations peuvent toutes être représentées comme un seul type de délégué. .: par exemple

private void RunOperationWithMainProgressFeedback(
    Func<bool> operation, 
    string startMessage, 
    string completionMessage, 
    string cancellationMessage, 
    string errorMessage) 
{ 
    this._UpdateMainForm.BeginInvoke(startMessage, false, null, null); 
    try 
    { 
     if (operation.Invoke()) 
     { 
      this._UpdateMainForm.BeginInvoke(completionMessage, true, null, null); 
     } 
     else 
     { 
      this._UpdateMainForm.BeginInvoke(cancellationMessage, true, null, null); 
     } 
    } 
    catch (Exception) 
    { 
     this._UpdateMainForm.BeginInvoke(errorMessage, true, null, null); 
     throw; 
    } 
} 

private void btnX_Click(object sender, EventArgs e) 
{ 
    this.RunOperationWithMainProgressFeedback(
     this.UpdateOperationA, 
     "StartA", 
     "CompletedA", 
     "CanceledA", 
     "ErrorA"); 
} 

Bien qu'il soit possible d'utiliser un dictionnaire pour stocker les valeurs d'argument (comme suggéré dans la réponse précédente de VinayC), ce n'est pas nécessaire. Personnellement, je l'éviterais pour des raisons de lisibilité et de performance, mais ymmv ...

+0

Cela semble beaucoup mieux, je vais mettre en œuvre cette approche et mettre à jour si je deviens quelque chose sauvage !! .. merci. –

2

Vous pouvez créer un dictionnaire/une liste contenant des détails pertinents pour chaque bouton. Ces détails incluent le nom de l'opération & délégué pour appeler l'opération, etc. Vous pouvez maintenant avoir un gestionnaire d'événement générique pour tous les boutons qui rechercheront le nom et l'opération de l'opération dans le dictionnaire.

Encore une autre façon serait de créer votre bouton personnalisé hérité du bouton et le bouton personnalisé peut avoir ces propriétés pour tenir au-dessus des détails. Choisissez selon votre goût.

+0

Intéressant. Avec la première approche, je suppose que la réflexion serait nécessaire pour appeler les méthodes par le nom stocké dans le dictionnaire. –

+0

Pas vraiment, vous pouvez stocker le délégué lui-même dans le dictionnaire. – VinayC

1

Je me suis fatigué de résoudre ce même problème encore et encore, j'ai donc écrit un ensemble de classes pour résoudre ce problème. L'idée de base est la suivante:

  • Créez un objet Progress progressif avec les champs qui vous intéressent, comme statusMessage.

  • Créez un objet Tâche abstrait. Job est comme un thread, mais en plus d'un Main et un Start, il a une méthode UpdateProgress (Progress p). UpdateProgress copie la progression en cours vers p. C'est généralement le seul endroit où les serrures seront acquises.

  • Dans votre utilisation particulière, sous-classe Progression et ajouter des champs si nécessaire. Puis sous-classe Job pour contenir les choses que vous voulez faire, et implémentez UpdateProgress.

  • Dans l'interface utilisateur, créez un temporisateur dont le travail d'appel principal. UpdateProgress puis met à jour l'interface utilisateur.

Souvent, les minuteries sont suffisamment bon pour les mises à jour et l'adoption d'un ui modèle pull-et-sync-ui est plus simple que de faire un modèle push-changements. Pull-and-sync-ui évite beaucoup de problèmes de threads croisés, et l'interface utilisateur entrer dans des états étranges. BONUS: Faites de votre objet Progress un objet INotifyProperyChanged et effectuez une liaison de données. (c'est pourquoi vous ne voudrez peut-être pas utiliser un IDictionary).

+0

Semble une approche assez lourde .. minuteries, verrous, threads .. peut fournir un pseudo-code pour plus de clarté? –

Questions connexes