Ejemplo de MVVM con transacciones y localizador de modelos de vista

Traducción aproximada del artículo MVVM with Transaction and View Locator Example publicado en inglés por Jeremy Likness el 26 de abril del 2010 en su blog C#er:Image.

 

Luego de mis dos artículos sobre transacciones en MVVM y otro patrón más de localización de modelos de vista, recibí numerosas solicitudes pidiendo un proyecto de ejemplo. Preguntaron, les escuché, y aquí lo tienen.

Voy a describir cómo produje el ejemplo. La mayoría del código proviene, sin alteraciones, de mis dos artículos anteriores. Para hacer más fácil el uso de transacciones he creado un comando.

 

public class TransactionCommand<T> : ICommand where T : BaseINPC
{
  private readonly TransactionModel<T> _transaction;
  private readonly bool _forCommit;

  public TransactionCommand(TransactionModel<T> transaction,
                            bool forCommit)
  {
    _transaction = transaction;
    _forCommit = forCommit;

    transaction.Value.PropertyChanged += (o, e) =>
                {
                  if (CanExecuteChanged != null)
                  {
                    CanExecuteChanged(this, EventArgs.Empty);
                  }
                };
  }

  public bool CanExecute(object parameter)
  {
    return _transaction.Value.IsDirty;
  }

  public void Execute(object parameter)
  {
    string message = _forCommit
      ? "Are you sure you wish to save your changes?"
      : "Are you sure you wish to cancel?" +
        "All changes will be lost.";

    var result = MessageBox.Show(message, "Please confirm",
                                 MessageBoxButton.OKCancel);
    if (result.Equals(MessageBoxResult.OK))
    {
      if (_forCommit)
      {
        _transaction.Value.IsDirty = false;
        _transaction.Commit();
      }
      else
      {
        _transaction.Rollback();
      }
    }
  }

  public event EventHandler CanExecuteChanged;
}

 

Normalmente crearía un cuadro de diálogo separado, pero por ser un ejemplo rápido lo dejé tal y como está. “Si no están de nuestra parte están en contra nuestra.” Bueno, no siempre, pero este comando toma el objeto al que está “escuchando” y guarda o cancela los cambios. El comando es habilitado automáticamente cuando los datos son alterados, lo cual es indicado por la propiedad IsDirty del modelo. Al ser ejecutado, el comando muestra la confirmación apropiada antes de guardar o cancelar los cambios.

Ya teniendo este útil comando puedo crear un modelo de vista base que se ajuste al tipo del modelo que vamos a empaquetar en una transacción. Tal como este:

 

public class ViewModelBase<T> : BaseINPC where T : BaseINPC
{
  private TransactionModel<T> _transaction; 

  public T EditableObject
  {
    get { return _transaction.Value; }
    set
    {
      RaisePropertyChanged(() => EditableObject);
      _Init(value);
    }
  }

  private void _Init(T instance)
  {
    _transaction = new TransactionModel<T>(instance);

    ConfirmCommand =
        new TransactionCommand<T>(_transaction, true);
    CancelCommand =
        new TransactionCommand<T>(_transaction, false);

    RaisePropertyChanged(()=>ConfirmCommand);
    RaisePropertyChanged(()=>CancelCommand);
  }

  public ICommand ConfirmCommand { get; set; }

  public ICommand CancelCommand { get; set; }
}

 

El modelo de vista expone la propiedad EditableObject. Cuando se le asigna un objeto, automáticamente lo envuelve en una transacción y enlaza los comandos apropiados para confirmar o cancelar cambios.

El modelo de vista siguiente crea un contacto de prueba (aquí es donde uno usaría un servicio o algún otro mecanismo para obtener los registros a editar):

 

[ViewModelForType("Contact")]
public class ContactViewModel : ViewModelBase<Contact>
{
  public ContactViewModel()
  {
    var contact = new Contact
                      {
                        FirstName = "Jeremy",
                        LastName = "Likness",
                        Email = "jlikness@wintellect.com",
                        PhoneNumber = "555-1212"
                      };

    contact.IsDirty = false;
    EditableObject = contact;
  }
}

 

No, el número de teléfono no es real. Noten cómo establezco el tipo como el de mi entidad Contact y luego lo único que hay que hacer es asignar el EditableObject. Mediante etiquetar el  modelo de vista ahora puedo asociarlo a la vista con la misma etiqueta. Ese es nuestro mecanismo de direccionamiento. Por tratarse solamente de una simple vista para el contacto, puedo configurar manualmente el ejemplo para que use el primer artículo de la lista de contactos. En una aplicación de mayor escala probablemente usaría un ItemsControl con una interfase de navegación que enlace los datos según se hagan visibles las vistas.

 

[Export]
public class ViewManager : IPartImportsSatisfiedNotification
{
  [ImportMany(AllowRecomposition = true)]
  public Lazy<UserControl, IViewForTypeCapabilities>[]
    Views { get; set; }

  [ImportMany(AllowRecomposition = true)]
  public Lazy<BaseINPC, IViewModelForTypeCapabilities>[]
    ViewModels { get; set; }

  [Import]
  public ContentControl MainRegion { get; set; }

  public void OnImportsSatisfied()
  {
    if (Views.Length > 0)
    {
      var view = Views[0].Value;
      MainRegion.Content = view;
      var viewModel =
        (from vm in ViewModels
         where vm.Metadata.TypeName.
               Equals(Views[0].Metadata.TypeName)
         select vm.Value).FirstOrDefault();
      view.DataContext = viewModel;
    }
  }
}

 

¡Eso es todo! Usando MEF para adherir las partes, obtenemos la aplicación mostrada abajo. Noten como la infraestructura se encarga de confirmar o deshacer cambios hechos a los datos. El desarrollador tan sólo tiene que asignar el modelo a la propiedad EditableObject. (Tal vez observarán que no he usado validadores de datos, pero el posible usarlos y hacer que verifiquen los datos en cada campo sin que interfieran con el sistema de transacciones.)

¡Buen Provecho!

 

Install Microsoft Silverlight

 

Recuerden que pueden bajar el código fuente.

 

Jeremy Likness

 

Etiquetas asignadas:
 

1 Respuesta » a “Ejemplo de MVVM con transacciones y localizador de modelos de vista”

  1. Alejandro dice:

    Gracias por el aporte, me sirvio mucho

Responder



Licencia de uso

El contenido de las traducciones está sujeto a los términos de protección de derechos de uso de los autores originales quienes han autorizado su publicación en este blog. Asegúrese de entender los terminos de la licencia de cada autor antes de usar tal contenido.

Mis propios artículos son publicados bajo los términos de la Licencia Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos de Creative Commons:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos License.

License

The contents of all translated articles is subject to the copyright and licensing terms of the original authors and has been published here with their express permission. Verify the original author's licensing terms before using the contents of these articles.

My own articles are licensed under the Creative Commons Attribution-Share Alike 3.0 United States License:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.