Procedura: aggiornare un modello UML da un thread in background
Talvolta può essere utile apportare modifiche a un modello in un thread in background.Se, ad esempio, si stanno caricando informazioni da una risorsa esterna lenta, è possibile utilizzare un thread in background per soprintendere agli aggiornamenti.In questo modo, l'utente può visualizzare ogni aggiornamento non appena si verifica.
È tuttavia necessario tenere presente che l'archivio UML non è thread-safe.Di seguito sono riportate alcune precauzioni importanti:
Ogni aggiornamento a un modello o a un diagramma deve essere eseguito nel thread dell'interfaccia utente.Il thread in background deve utilizzare Invoke o Invoke per fare in modo che il thread dell'interfaccia utente esegua gli aggiornamenti effettivi.
Se si raggruppa una serie di modifiche in un'unica transazione, è consigliabile non consentire la modifica del modello da parte dell'utente mentre la transazione è in corso.In caso contrario, qualsiasi modifica apportata dall'utente diventerà parte della stessa transazione.È possibile impedire all'utente di apportare modifiche tramite la visualizzazione di una finestra di dialogo modale.Se lo si desidera, è possibile visualizzare un pulsante Annulla nella finestra di dialogo.L'utente può visualizzare le modifiche man mano che si verificano.
Esempio
In questo esempio viene utilizzato un thread in background per apportare diverse modifiche a un modello.Viene utilizzata una finestra di dialogo per escludere l'utente mentre il thread è in esecuzione.In questo semplice esempio non viene fornito alcun pulsante Annulla nella finestra di dialogo.Questa funzionalità può tuttavia essere aggiunta in modo facile.
Per eseguire l'esempio
Creare un gestore di comandi in un progetto C# come descritto in Procedura: definire un comando di menu in un diagramma di modellazione.
Verificare che il progetto includa riferimenti a questi assembly:
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
Aggiungere il progetto a un Windows Form denominato ProgressForm.Tale oggetto deve visualizzare un messaggio che indica che sono in corso gli aggiornamenti.Non è necessario che includa altri controlli.
Aggiungere un file C# contenente il codice illustrato dopo il passaggio 7.
Compilare ed eseguire il progetto.
Una nuova istanza di Visual Studio verrà avviata in modalità sperimentale.
Creare o aprire un diagramma classi UML nell'istanza sperimentale di Visual Studio.
Fare clic con il pulsante destro del mouse in un punto qualsiasi del diagramma classi UML, quindi scegliere l'opzione per l'aggiunta di diverse classi UML.
Nel diagramma verranno visualizzate diverse nuove caselle di classi, una dopo l'altra a intervalli di mezzo secondo.
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"; }
}
}
}
Per consentire all'utente di annullare il thread nell'esempio
Aggiungere un pulsante Annulla alla finestra di stato.
Aggiungere il codice seguente alla finestra di stato:
public event MethodInvoker Cancel;
private void CancelButton_Click(object sender, EventArgs e)
{
Cancel();
}
Nel metodo Execute() inserire questa riga dopo la costruzione del form:
form.Cancel += delegate() { worker.CancelAsync(); };
Altri metodi di accesso al thread dell'interfaccia utente
Se non si desidera creare una finestra di dialogo, è possibile accedere al controllo che consente la visualizzazione del diagramma:
DiagramView uiThreadHolder = context.CurrentDiagram.GetObject<Diagram>().ActiveDiagramView;
È possibile utilizzare uiThreadHolder.Invoke() per eseguire operazioni nel thread dell'interfaccia utente.
Vedere anche
Concetti
Procedura: definire un comando di menu in un diagramma di modellazione
Procedura: definire un gestore movimenti in un diagramma di modellazione