Otro patrón de localización de modelos de vista

Traducción aproximada del artículo Yet Another MVVM Locator Pattern publicado en inglés por Jeremy Likness el 23 de abril del 2010 en su blog C#er:Image.

 

He estado trabajando con varios clientes combinando MVVM con MEF y siempre surge el problema de cómo unir vistas y modelos de vistas. Varios han tratado el tema, desde mi propio patrón de localización de modelos de vista, hasta el más avanzado (y elegante) localizador propuesto por John Papa y compañía.

Este es otro sistema que les puede ser útil. La mayoría de las veces las vistas son usadas en conjunto con algún modelo de navegación, ya sea la infraestructura provista por Silverlight o algún mecanismo creado por nosotros para el manejo de regiones. Lo suave de usar MEF en estos casos es que el operador Lazy evita conectar el control hasta que sea necesario. Si usamos este patrón, con un poco de “magia” podemos asociar la vista y el modelo de vista sin ningún tipo de acoplamiento.

Lo primero es crear un atributo especializado para la vista y la interfase correspondiente para que MEF pueda encontrar la información necesaria.

 

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ViewOfTypeAttribute : ExportAttribute
{
  public string TypeName { get; set; }

  public ViewOfTypeAttribute(string typeName)
    : base(typeof(UserControl))
  {
    TypeName = typeName;
  }
}

public interface IViewOfTypeCapabilities
{
  string TypeName { get; }
}

 

Esto nos permite usar una “cadena mágica” para etiquetar la vista. Si lo prefieren, se puede usar una enumeración para que tenga un tipo de datos definido, pero la cadena nos da la flexibilidad de poder usar más adelante que otros módulos que todavía no conozcamos. Usando este sistema, mi vista para contactos se puede etiquetar de esta forma:

 

[ViewOfType("Contact")]
public partial class ContactView : UserControl
{
  public ContactView()
  {
    InitializeComponent();
  }
}

 

Luego creamos otro atributo para etiquetar el modelo de vista. La mayoría de las implementaciones de MVVM usan un modelo de vista base que se encarga de asuntos como disparar INotifyPropertyChanged y así por el estilo. Usando una clase base, es fácil crear un atributo en el modelo de vista como este:

 

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ViewModelOfTypeAttribute : ExportAttribute
{
  public string TypeName { get; set; }

  public ViewModelOfTypeAttribute(string typeName)
    : base(typeof(ViewModelBase))
  {
    TypeName = typeName;
  }
}

public interface IViewModelOfTypeCapabilities
{
  string TypeName { get; }
}

 

¿Les parece familiar? Debería, ya que así fue como dirigimos las vistas a los modelos de vista. Así aun cuando haya múltiples vistas usando el mismo modelo de vista, podemos enlazarlas todas. Cuando codifico mi modelo de vista, lo único que tengo que hacer es exportarlo con la etiqueta apropiada:

 

[ViewModelOfType("Contact")]
public class ContactViewModel : ViewModelBase
{
}

 

Noten cómo la cadena que uso es la misma en la vista y el modelo de vista. ¡Tiene que ser idéntica para poder asociarlos! Por cierto que he dejado por fuera el asunto de la implementación de la navegación. Hay varias formas de hacerlo, desde intercambiar el contenido de un ContentControl hasta usar un ItemsControl y jugar con la visibilidad de sus elementos. Sea como sea, en algún momento hay que crear una instancia de la vista. Es durante la creación cuando es asociada automáticamente con el modelo de vista, si es que ya está disponible. Este trozo de código ha de darles la idea general:

 

[Export]
public class Binder
{
  [ImportMany(AllowRecomposition=true)]
  public Lazy<UserControl,IViewOfTypeCapabilities>[]
  Views { get; set; }

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

  private _Bind(string binding)
  {
    var viewModel =
        (from vm in ViewModels
         where vm.Metadata.TypeName.Equals(binding)
         select vm.Value).FirstOrDefault();

    foreach(var view in
        (from v in Views
         where v.Metadata.TypeName.Equals(binding)
         select v.Value).ToArray())
    {
      view.DataContext = viewModel;
    }
  }
}

 

El método _Bind es llamado la primera vez que la vista es incorporada en una superficie (por ejemplo, es agregada a un control).

De esta manera podemos olvidar todo el alboroto con XAML o código subyacente. ¡Simplemente usamos la misma etiqueta en la vista y el modelo de vista dejando que sirva de pegamento entre ambos!

 

Jeremy Likness

 

Etiquetas asignadas:
 

1 Respuesta » a “Otro patrón de localización de modelos de vista”

  1. [...] 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í [...]

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.