Compartilhar via


Como: Atualizar um modelo UML a partir de um Thread de plano de fundo

Às vezes pode ser útil fazer alterações em um modelo em um thread de segundo plano. Por exemplo, se você estiver carregando informações de um recurso externo lento, poderia usar um thread de segundo plano para supervisionar as atualizações. Isso permite que o usuário veja cada atualização assim que ele acontece.

No entanto, você deve estar ciente de que o armazenamento UML não é thread-safe. As seguintes precauções são importantes:

  • Cada atualização de um modelo ou diagrama deve ser feita no thread da interface (UI) do usuário. O segmento de plano de fundo deve usar ControlInvoke() ou DispatcherInvoke() para que o segmento de interface do usuário realizar atualizações real.

  • Se você agrupar uma série de alterações em uma única transação, recomendamos que você impedir que o usuário editar o modelo, enquanto a transação está em andamento. Caso contrário, as edições feitas pelo usuário se tornará parte da mesma transação. Você pode impedir que o usuário fazer alterações, mostrando uma caixa de diálogo modal. Se desejar, você pode fornecer um botão Cancelar na caixa de diálogo. O usuário pode ver as alterações conforme eles ocorrem.

Exemplo

Este exemplo usa um thread de segundo plano para fazer várias alterações com um modelo. Uma caixa de diálogo é usada para excluir o usuário enquanto o thread está sendo executado. Neste exemplo simples, nenhum botão Cancelar é fornecido na caixa de diálogo. No entanto, seria fácil adicionar esse recurso.

Para executar o exemplo.

  1. Criar um manipulador de comandos em um projeto C#, conforme descrito em Como: Definir um comando de Menu em um diagrama de modelagem.

  2. Certifique-se de que o projeto inclui referências a esses assemblies:

    • 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. Adicione ao projeto um Windows form chamado ProgressForm. Ele deve exibir uma mensagem informando que as atualizações estão em andamento. Ele não tem de ter quaisquer outros controles.

  4. Adicione um arquivo C# que contém o código que é mostrado após a etapa 7.

  5. Criar e executar o projeto.

    Uma nova instância de Visual Studio será iniciado no modo de experimental.

  6. Crie ou abra um diagrama de classe UML na instância experimental do Visual Studio.

  7. Clique com o botão direito em qualquer lugar no diagrama de classe UML e clique em Adicionar várias Classes UML.

Várias novas caixas de classe aparecerá no diagrama, um após o outro em intervalos de meio segundo.

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"; }
    }
  }
}

Para permitir que o usuário cancele o encadeamento no exemplo

  1. Adicione um botão Cancelar na caixa de diálogo de progresso.

  2. Adicione o seguinte código para a caixa de diálogo de progresso:

    public event MethodInvoker Cancel;

    private void CancelButton_Click(object sender, EventArgs e)

    {

    Cancel();

    }

  3. No método Execute(), insira esta linha após a construção do formulário:

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

Outros métodos para acessar o segmento de interface do usuário

Se não desejar criar uma caixa de diálogo, você pode acessar o controle que exibe o diagrama:

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

Você pode usar uiThreadHolder.Invoke() para realizar operações no thread da interface do usuário.

Consulte também

Conceitos

Como: Definir um comando de Menu em um diagrama de modelagem

Outros recursos

Como: Definir uma queda e clique duas vezes o manipulador de um diagrama de modelagem