Поделиться через


Пошаговое руководство. Обновление модели UML из фонового потока

Иногда бывает полезно делать изменения в модели в фоновом потоке.Например, при загрузке сведений из медленного внешнего ресурса, можно использовать фоновый поток для надзора за обновлениями.Это позволит пользователю видеть обновления по мере их появления.

Однако следует помнить, что хранилище UML не потокобезопасно.Важно принять следующие меры предосторожности.

  • Каждое обновление модели или схемы должно выполняться в потоке пользовательского интерфейса.Фоновый поток должен использовать метод Invoke или метод Invoke, чтобы поток пользовательского интерфейса выполнил текущее обновление.

  • При группировке последовательности изменений в одной транзакции рекомендуется исключить для пользователя возможность изменять модель во время выполнения транзакции.В противном случае все изменения, совершенные пользователем, станут частью одной транзакции.Предотвратить вмешательство пользователя можно, вызывав модальное диалоговое окно.Можно предоставить в диалоговом окне кнопку "Отмена".Пользователь может видеть изменения по мере того, как они происходят.

Пример

Этот пример использует фоновый поток для выполнения нескольких изменений в модели.Диалоговое окно используется, чтобы исключить вмешательство пользователя во время работы потока.В этом простом примере диалоговое окно не содержит кнопки "Отмена".Однако добавить эту функцию несложно.

Для запуска примера

  1. Создайте обработчик команды в проекте C#, как это описано в разделе Практическое руководство. Определение команды меню на схеме моделирования.

  2. Убедитесь, что проект включает ссылки на следующие сборки.

    • Microsoft.VisualStudio.ArchitectureTools.Extensibility

    • Microsoft.VisualStudio.Modeling.Sdk.11.0

    • Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0

    • Microsoft.VisualStudio.Uml.Interfaces

    • System.ComponentModel.Composition

    • System.Windows.Forms

  3. Добавьте в проект форму Windows с названием ProgressForm.Она должна показывать сообщение о том, что идет процесс обновления.Она не должна содержать других элементов управления.

  4. Добавьте файл C#, содержащий код, приведенный после шага 7.

  5. Выполните построение и запуск проекта.

    Новый экземпляр Visual Studio запустится в экспериментальном режиме.

  6. Создайте или откройте UML-схему классов в экспериментальном экземпляре Visual Studio.

  7. Щелкните правой кнопкой в любом месте UML-схемы классов, затем щелкните Добавить несколько UML-классов.

Несколько новых классов отобразится на схеме, один за другим с интервалом в полсекунды.

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 // CHANGE TO YOUR NAMESPACE
{
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  class UmlClassAdderCommand : ICommandExtension
  {

    [Import]
    IDiagramContext context { 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((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)
    {
      command.Enabled = command.Visible = true;
    }

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

Как разрешить пользователю отменить поток из примера

  1. Добавьте кнопку отмены в диалоговое окно, отображающее ход выполнения.

  2. Добавьте в диалоговое окно следующую строку кода.

    public event MethodInvoker Cancel;

    private void CancelButton_Click(object sender, EventArgs e)

    {

    Cancel();

    }

  3. В метод Execute() после конструкции формы добавьте следующую строку.

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

Ee941659.collapse_all(ru-ru,VS.110).gifДругие методы доступа к потоку пользовательского интерфейса

Если нет необходимости создавать диалоговое окно, можно получить доступ к элементу управления, который отображает схему.

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

Для выполнения операций в потоке пользовательского интерфейса можно использовать метод uiThreadHolder.Invoke().

См. также

Основные понятия

Практическое руководство. Определение команды меню на схеме моделирования

Практическое руководство. Определение обработчика жестов на схеме моделирования