Partager via


Comment : mettre à jour un modèle UML à partir d'un thread d'arrière-plan

Il peut parfois s'avérer utile d'apporter des modifications à un modèle dans un thread d'arrière-plan. Par exemple, lorsque vous chargez des informations à partir d'une ressource externe lente, vous pouvez utiliser un thread d'arrière-plan pour superviser les mises à jour. Cela permet à l'utilisateur de consulter chaque mise à jour dès qu'elle se produit.

Vous devez toutefois savoir que le magasin UML n'est pas thread-safe. Les précautions suivantes sont importantes :

  • Chaque mise à jour d'un modèle ou d'un diagramme doit être effectuée dans le thread d'interface utilisateur. Le thread d'arrière-plan doit utiliser ControlInvoke() ou DispatcherInvoke() pour que le thread d'interface utilisateur effectue réellement les mises à jour.

  • Si vous regroupez une série de modifications dans une seule transaction, nous vous recommandons d'empêcher l'utilisateur de modifier le modèle pendant que la transaction est en cours. Sinon, toutes les modifications effectuées par l'utilisateur feront partie de la même transaction. Vous pouvez empêcher l'utilisateur d'apporter des modifications en affichant une boîte de dialogue modale. Si vous le souhaitez, vous pouvez ajouter un bouton Annuler à cette boîte de dialogue. L'utilisateur peut consulter les modifications à mesure qu'elles sont apportées.

Exemple

Cet exemple utilise un thread d'arrière-plan pour apporter plusieurs modifications à un modèle. Une boîte de dialogue est utilisée pour exclure l'utilisateur pendant l'exécution du thread. Dans cet exemple simple, aucun bouton Annuler n'est ajouté à la boîte de dialogue. Il serait toutefois facile d'ajouter cette fonctionnalité.

Pour exécuter l'exemple

  1. Créez un gestionnaire de commandes dans un projet C# comme indiqué dans Comment : définir une commande de menu sur un diagramme de modélisation.

  2. Assurez-vous que le projet inclut des références aux assemblys suivants :

    • Microsoft.VisualStudio.ArchitectureTools.Extensibility

    • Microsoft.VisualStudio.Modeling.Sdk.10.0

    • Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0

    • Microsoft.VisualStudio.Uml.Interfaces

    • System.ComponentModel.Composition

    • System.Windows.Forms

  3. Ajoutez au projet un Windows Form nommé ProgressForm. Il doit afficher un message qui indique que les mises à jour sont en cours. Aucun autre contrôle n'est nécessaire.

  4. Ajoutez un fichier C# qui contient le code indiqué après l'étape 5.

  5. Générez et exécutez le projet.

    Une nouvelle instance de Visual Studio démarre en mode expérimental.

  6. Créez ou ouvrez un diagramme de classes UML dans l'instance expérimentale de Visual Studio.

  7. Cliquez avec le bouton droit sur le diagramme de classes UML, puis cliquez sur Ajouter plusieurs classes UML.

Plusieurs nouvelles zones de classes s'afficheront dans le diagramme, les unes après les autres à une demie seconde d'intervalle.

using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows.Forms;

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.Uml.Classes;

namespace BackgroundThreadProgressUI
{
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  class UmlClassAdderCommand : ICommandExtension
  {

    [Import]
    IDiagramContext context { get; set; }

    [Import]
    IServiceProvider serviceProvider { get; set; }

    [Import]
    ILinkedUndoContext linkedUndoContext { get; set; }

    // Called when the user runs the command.
    public void Execute(IMenuCommand command)
    {
      // The form that will exclude the user.
      ProgressForm form = new ProgressForm();

      // System.ComponentModel.BackgroundWorker is a
      // convenient way to run a background thread.
      BackgroundWorker worker = new BackgroundWorker();
      worker.WorkerSupportsCancellation = true;

      worker.DoWork += delegate(object sender, DoWorkEventArgs args)
      {
        // This block will be executed in a background thread.

        IClassDiagram diagram = context.CurrentDiagram as IClassDiagram;
        IModelStore store = diagram.ModelStore;
        const int CLASSES_TO_CREATE = 15;

        // Group all the changes together.
        using (ILinkedUndoTransaction transaction = linkedUndoContext.BeginTransaction("Background Updates"))
        {
          for (int i = 1; i < CLASSES_TO_CREATE; i++)
          {
            if (worker.CancellationPending) 
               return; // No commit - undo all.

            // Create model elements using the UI thread by using
            // the Invoke method on the progress form. Always 
            // modify the model and diagrams from a UI thread.
            form.Invoke(new MethodInvoker(delegate()
            {
              IClass newClass = store.Root.CreateClass();
              newClass.Name = string.Format("NewClass{0}", i);
              diagram.Display(newClass);
            }));
            

            // Sleep briefly so that we can watch the updates.
            Thread.Sleep(500);
          }
          
          // Commit the transaction or it will be rolled back.
          transaction.Commit();
        }
      };

      // Close the form when the thread completes.
      worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs args)
      {
        form.Close();
      };

      // Start the thread before showing the modal progress dialog.
      worker.RunWorkerAsync();

      // Show the form modally, parented on VS.
      // Prevents the user from making changes while in progress.
      form.ShowDialog();
    }

    public void QueryStatus(IMenuCommand command)
    {
    }

    public string Text
    {
      get { return "Add several classes"; }
    }
  }
}

Pour permettre à l'utilisateur d'annuler le thread dans l'exemple

  1. Ajoutez un bouton Annuler à la boîte de dialogue de progression.

  2. Ajoutez le code suivant à la boîte de dialogue de progression :

    public event MethodInvoker Cancel;

    private void CancelButton_Click(object sender, EventArgs e)

    {

    Cancel();

    }

  3. Dans la méthode Execute(), insérez cette ligne après la construction du formulaire :

    form.Cancel += delegate() { worker.CancelAsync(); };

Autres méthodes d'accès au thread d'interface utilisateur

Si vous ne souhaitez pas créer de boîte de dialogue, vous peut accéder au contrôle qui affiche le diagramme :

DiagramView uiThreadHolder = context.CurrentDiagram.GetObject<Diagram>().ActiveDiagramView;

Vous pouvez utiliser uiThreadHolder.Invoke() pour exécuter des opérations dans le thread d'interface utilisateur.

Voir aussi

Autres ressources

Comment : définir une commande de menu sur un diagramme de modélisation

Comment : définir un gestionnaire déposer et double-cliquer sur un diagramme de modélisation