共用方式為


HOW TO:從背景執行緒更新 UML 模型

有時候,以背景執行緒變更模型會很有用。 例如,如果您要從速度緩慢的外部資源載入資訊,您可以使用背景執行緒來監督更新作業。 這可讓使用者立即看到每項發生的更新。

但請務必留意,UML 存放區並不具備執行緒安全。 以下是重要的預防措施:

  • 對模型或圖表所做的每項更新,都必須在使用者介面 (UI) 執行緒中進行。 背景執行緒必須使用 ControlInvoke() 或 DispatcherInvoke() 讓 UI 執行緒執行實際更新。

  • 如果您將一系列的變更變成單一交易,建議您讓使用者無法在交易進行時編輯模型。 否則,使用者所做的任何編輯都將成為該交易的一部分。 您可以顯示強制回應對話方塊,讓使用者無法進行變更。 如果有需要,您可以在對話方塊中提供 [取消] 按鈕。 使用者可以看見發生中的變更。

範例

這個範例會使用背景執行緒對模型進行數項變更。 在執行緒執行期間,會使用對話方塊排除使用者的動作。 在這個簡單範例中,並未在對話方塊中提供 [取消] 按鈕。 但要加入該功能並不難。

若要執行範例

  1. 在 C# 專案中建立命令處理常式,如HOW TO:在模型圖表上定義功能表命令中的說明。

  2. 確定專案中包含下列組件的參考:

    • 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. 將名為 ProgressForm 的 Windows 表單加入至專案。 裡面應顯示一則訊息,指出更新正在進行中。 裡面不需要有其他任何控制項。

  4. 加入 C# 檔案,其中包含在步驟 7 之後顯示的程式碼。

  5. 建置及執行專案。

    Visual Studio 的新執行個體會以實驗模式啟動。

  6. 在 Visual Studio 的實驗執行個體中建立或開啟 UML 類別圖表。

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

若要允許使用者取消範例中的執行緒

  1. 將取消按鈕加入至進度對話方塊。

  2. 將下列程式碼加入至進度對話方塊:

    public event MethodInvoker Cancel;

    private void CancelButton_Click(object sender, EventArgs e)

    {

    Cancel();

    }

  3. 在 Execute() 方法中,於表單的建構之後插入下列一行:

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

其他存取 UI 執行緒的方法

如果您不要建立對話方塊,您可以存取顯示圖表的控制項:

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

您可以使用 uiThreadHolder.Invoke() 執行 UI 執行緒中的作業。

請參閱

其他資源

HOW TO:在模型圖表上定義功能表命令

HOW TO:在模型圖表上定義 Drop 和 Double-Click 處理常式