Cómo: Actualizar un modelo UML a partir de un subproceso en segundo plano
En ocasiones, puede resultar útil realizar cambios en un modelo a través de un subproceso en segundo plano. Por ejemplo, si está cargando información desde un recurso externo que es lento, puede usar un subproceso en segundo plano para supervisar las actualizaciones. De este modo, el usuario podrá ver cada actualización tan pronto como se produzca.
Sin embargo, debe tener en cuenta que el almacén UML no es seguro para subprocesos. Las siguientes consideraciones son importantes:
Todas las actualizaciones de un modelo o diagrama deben realizarse en el subproceso de la interfaz de usuario. El subproceso en segundo plano debe usar ControlInvoke() o DispatcherInvoke() para hacer que sea el subproceso de la interfaz de usuario el que realmente realice las actualizaciones.
Si agrupa una serie de cambios en una única transacción, le recomendamos que impida que el usuario edite el modelo mientras la transacción esté en curso. De lo contrario, cualquier modificación que pudiera hacer el usuario pasaría a formar parte de la misma transacción. Para evitar que el usuario realice cambios, muestre un cuadro de diálogo modal. Si lo desea, puede incluir un botón Cancelar en el cuadro de diálogo. El usuario puede ver los cambios cuando se producen.
Ejemplo
En este ejemplo se usa un subproceso en segundo plano para realizar diversos cambios en un modelo. Se usa un cuadro de diálogo para excluir al usuario mientras el subproceso está en ejecución. En este sencillo ejemplo, no se incluye el botón Cancelar en el cuadro de diálogo. Sin embargo, sería fácil agregar esa característica.
Para ejecutar el ejemplo
Cree un controlador de comando en un proyecto de C# tal y como se describe en Cómo: Definir un comando de menú en un diagrama de modelado.
Asegúrese de que el proyecto contiene referencias a estos ensamblados:
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
Agregue al proyecto un nuevo formulario de Windows denominado ProgressForm. Debe aparece un mensaje en el que se indique que las actualizaciones están en curso. No es necesario que contengan ningún otro control.
Agregue un archivo C# que contenga el código que se muestra después del paso 7.
Compile y ejecute el proyecto.
Se iniciará una nueva instancia de Visual Studio en modo experimental.
Cree o abra un diagrama de clases UML en la instancia experimental de Visual Studio.
Haga clic con el botón secundario en cualquier parte del diagrama de clases UML y, a continuación, haga clic en Agregar varias clases UML.
Aparecerán varios cuadros de clases nuevos en el diagrama, uno detrás de otro, en intervalos de medio 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 el usuario pueda cancelar el subproceso del ejemplo
Agregue un botón de cancelación en el cuadro de diálogo de progreso.
Agregue el código siguiente al cuadro de diálogo de progreso:
public event MethodInvoker Cancel;
private void CancelButton_Click(object sender, EventArgs e)
{
Cancel();
}
En el método Execute (), inserte esta línea tras la construcción del formulario:
form.Cancel += delegate() { worker.CancelAsync(); };
Otros métodos para obtener acceso al subproceso de la interfaz de usuario
Si no desea crear un cuadro de diálogo, puede obtener acceso al control que muestra el diagrama:
DiagramView uiThreadHolder = context.CurrentDiagram.GetObject<Diagram>().ActiveDiagramView;
Puede usar uiThreadHolder.Invoke() para realizar operaciones en el subproceso de la interfaz de usuario.
Vea también
Otros recursos
Cómo: Definir un comando de menú en un diagrama de modelado
Cómo: Definir un controlador de colocación y doble clic en un diagrama de modelado