有時候,以背景執行緒變更模型會很有用。 例如,如果您要從速度緩慢的外部資源載入資訊,您可以使用背景執行緒來監督更新作業。 這可讓使用者立即看到每項發生的更新。
但請務必留意,UML 存放區並不具備執行緒安全。 以下是重要的預防措施:
對模型或圖表所做的每項更新,都必須在使用者介面 (UI) 執行緒中進行。 背景執行緒必須使用 ControlInvoke() 或 DispatcherInvoke() 讓 UI 執行緒執行實際更新。
如果您將一系列的變更變成單一交易,建議您讓使用者無法在交易進行時編輯模型。 否則,使用者所做的任何編輯都將成為該交易的一部分。 您可以顯示強制回應對話方塊,讓使用者無法進行變更。 如果有需要,您可以在對話方塊中提供 [取消] 按鈕。 使用者可以看見發生中的變更。
範例
這個範例會使用背景執行緒對模型進行數項變更。 在執行緒執行期間,會使用對話方塊排除使用者的動作。 在這個簡單範例中,並未在對話方塊中提供 [取消] 按鈕。 但要加入該功能並不難。
若要執行範例
在 C# 專案中建立命令處理常式,如HOW TO:在模型圖表上定義功能表命令中的說明。
確定專案中包含下列組件的參考:
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
將名為 ProgressForm 的 Windows 表單加入至專案。 裡面應顯示一則訊息,指出更新正在進行中。 裡面不需要有其他任何控制項。
加入 C# 檔案,其中包含在步驟 7 之後顯示的程式碼。
建置及執行專案。
Visual Studio 的新執行個體會以實驗模式啟動。
在 Visual Studio 的實驗執行個體中建立或開啟 UML 類別圖表。
以滑鼠右鍵按一下 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"; }
}
}
}
若要允許使用者取消範例中的執行緒
將取消按鈕加入至進度對話方塊。
將下列程式碼加入至進度對話方塊:
public event MethodInvoker Cancel;
private void CancelButton_Click(object sender, EventArgs e)
{
Cancel();
}
在 Execute() 方法中,於表單的建構之後插入下列一行:
form.Cancel += delegate() { worker.CancelAsync(); };
其他存取 UI 執行緒的方法
如果您不要建立對話方塊,您可以存取顯示圖表的控制項:
DiagramView uiThreadHolder = context.CurrentDiagram.GetObject<Diagram>().ActiveDiagramView;
您可以使用 uiThreadHolder.Invoke() 執行 UI 執行緒中的作業。