Usar el patrón modelo-vista-modelo de vista (MVVM) en Hilo (aplicaciones de la Tienda Windows con C++ y XAML)

Applies to Windows only

De: Desarrollar una aplicación de la Tienda Windows de principio a fin con C++ y XAML: Hilo

El logotipo de modelos y prácticas

Página anterior | Página siguiente

En las primeras fases del proyecto decidimos adoptar el patrón modelo-vista-modelo de vista (MVVM) para la arquitectura de Hilo. Nos atrajo el hecho de que el patrón MVVM facilita el mantenimiento y las pruebas de las aplicaciones de la Tienda Windows con C++ y XAML, especialmente a medida que crece. MVVM es un patrón relativamente nuevo para aplicaciones de C++.

Descarga

Descargar la muestra Hilo
Descargar el libro (PDF)

Después de descargar el código, consulta el tema de introducción a Hilo para obtener instrucciones.

Aprenderás

  • Cómo las aplicaciones de la Tienda Windows se pueden beneficiar de MVVM.
  • Las técnicas recomendadas para aplicar el patrón MVVM a las aplicaciones de la Tienda Windows.
  • Cómo asignar vistas a elementos de la interfaz de usuario.
  • Cómo compartir modelos de vista entre las vistas.
  • Cómo ejecutar comandos en un modelo de vista.

Se aplica a

  • Windows en tiempo de ejecución para Windows 8
  • Extensiones de componentes de Visual C++ (C++/CX)
  • XAML

¿Qué es MVVM?

MVVM es un patrón arquitectónico. Es una especialización del patrón de modelo de presentación introducido por Martin Fowler. También está relacionado con el patrón modelo-vista-controlador (MVC) y el patrón modelo-vista-presentador (MVP) que quizás ya conozcas.

Una aplicación que usa MVVM separa la lógica empresarial, la interfaz de usuario y el comportamiento de la presentación.

  • Los modelos representan el estado y las operaciones de los objetos empresariales que tu aplicación manipula. Por ejemplo, Hilo lee y modifica archivos de imagen, por lo que tiene sentido que los tipos de datos de los archivos de imagen y las operaciones que se realizan con archivos de imagen formen parte del modelo de Hilo.
  • Las vistas contienen elementos de la interfaz de usuario, e incluyen todo el código que implementa la experiencia del usuario de la aplicación. Una vista define la estructura, el diseño y la apariencia de lo que el usuario ve en pantalla. Cuadrículas, páginas, botones y cuadros de texto son algunos ejemplos de los elementos que los objetos de vista administran.
  • Los modelos de vista encapsulan el estado, las acciones y las operaciones de la aplicación. Un modelo de vista sirve como nivel de desacoplamiento entre el modelo y la vista. Proporciona los datos con u n formato que la vista pueda utilizar y actualiza el modelo para que la vista no tenga que interactuar con el modelo. Los modelos de vista responden a los comandos y desencadenan eventos. También actúan como orígenes de cualquier dato que las vistas muestran. Los modelos de vista se crean específicamente para admitir una vista. Puedes pensar en un modelo de vista como en la aplicación menos la interfaz de usuario. En las aplicaciones de la Tienda Windows, puedes enlazar mediante declaración las vistas con sus correspondientes modelos de vista.

Estas son las relaciones entre una vista, un modelo de vista y un modelo.

Relaciones de una vista, un modelos de vista y un modelo

[Arriba]

MVVM en Hilo

En Hilo, hay una clase de vista por cada página de la interfaz de usuario. (Una página es una instancia de la clase Windows::UI::Xaml::Controls::Page). Cada vista tiene una clase de modelo de vista correspondiente. Todos los modelos de vista de Hilo comparten el modelo de dominio de la aplicación, al que se suele llamar simplemente el modelo. El modelo está compuesto por clases que los modelos de vista utilizan para implementar la funcionalidad de la aplicación.

Nota  Si quieres pasar directamente al tutorial sobre el código de la vista y el modelo de vista de Hilo, consulta Crear páginas y navegar por ellas en Hilo.

En la solución de Visual Studio Hilo.sln, hay carpetas de solución con el nombre de cada uno de los niveles de MVVM.

Carpetas de solución de Visual Studio
  • La carpeta Models contiene archivos .cpp (C++) y .h (encabezado de C++) que conforman el modelo de Hilo.
  • La carpeta Views contiene clases de interfaz de usuario y archivos XAML.
  • La carpeta ViewModels contiene los archivos .cpp y .h de las clases del modelo de vista de la aplicación.

Con el patrón MVVM, el enlace de datos XAML permite a un modelo de vista actuar como contexto de datos de una página. Un contexto de datos es responsable de proporcionar las propiedades que suministran a la vista los datos para los elementos de interfaz de usuario que hay en la página.

Nota  No es necesario que uses el enlace de datos para conectar las vistas y los modelos de vista. También se puede usar un archivo de código subyacente que contenga el código de C++ que está asociado con las clases de página. Reconocerás los archivos de código subyacente porque usan el sufijo .xaml.cpp. Por ejemplo, en Hilo el archivo MainHubView.xaml.cpp es el archivo de código subyacente de la página definida por el archivo MainHubView.xaml. Muchas herramientas de diseño visual como Microsoft Expression están optimizadas para su uso con el enlace de datos.

Los modelos de vista se conectan al modelo subyacente de la aplicación mediante llamadas a métodos de instancia. No se necesita un enlace especial para realizar estas llamadas. Si quieres una separación bien diferenciada entre el modelo y los modelos de vista de la aplicación, puedes empaquetar las clases del modelo en una biblioteca diferente. Hilo no usa una biblioteca diferente para su modelo. En su lugar, guarda los archivos que definen las clases del modelo en una carpeta diferente del proyecto Hilo de Visual Studio.

[Arriba]

¿Por qué usar MVVM para Hilo?

Hay dos enfoques de implementación principales para una interfaz de usuario: usar un archivo de código subyacente para la lógica de presentación o separar la lógica de presentación y la estructura de la interfaz de usuario con un patrón como, por ejemplo, MVVM. Después de analizar nuestras necesidades, elegimos el enfoque MVVM para Hilo porque:

  • Quisimos probar nuestra lógica de presentación. MVVM permite separar limpiamente la lógica de vista de los controles de la interfaz de usuario, y esto es importante para la automatización de las pruebas.
  • Queríamos asegurarnos de que la lógica de vista y de presentación pudieran evolucionar independientemente y reducir las dependencias entre los diseñadores y desarrolladores de la experiencia del usuario. MVVM, utilizado con el enlace de datos de XAML, hace que esto sea posible.

[Arriba]

Para obtener más información

Encontrarás más información acerca de MVVM en línea. Estos son algunos ejemplos en código administrado, pero los conceptos también son aplicables a C++:

[Arriba]

Variaciones del patrón MVVM

El patrón MVVM se puede personalizar de varias maneras. Veamos algunas de ellas.

Asignar vistas a elementos de la interfaz de usuario que no sean páginas

En Hilo, cada clase de página es un objeto de vista MVVM, y todas las vistas MVVM son páginas. Pero no tienes que hacer lo mismo. Por ejemplo, una vista podría ser una DataTemplate para un objeto en un ItemsControl.

Compartir modelos de vista entre varias vistas

Una vista puede tener su propio modelo de vista o puede compartir el modelo de otra vista. La elección depende de si las vistas comparten mucha funcionalidad común. Para simplificar, en Hilo cada vista está asociada a un modelo de vista único.

Ejecutar comandos en un modelo de vista

Puedes usar el enlace de datos para los botones y otros controles de la interfaz de usuario que hacen que la aplicación realice operaciones. Si el control es un Origen del comando, la propiedad Command del control está enlazada a datos a una propiedad ICommand del modelo de vista. Cuando se invoca el comando del control, el código del modelo de vista se ejecuta. Recomendamos que utilices el enlace de datos para comandos cuando uses MVVM.

Este es un ejemplo de la ejecución de un comando desde Hilo. La página de girar imagen contiene un elemento de interfaz de usuario para el botón Guardar archivo. Este código XAML procede del archivo RotateImageView.xaml.


<Button x:Name="SaveButton"
        x:Uid="AcceptAppBarButton"
        Command="{Binding SaveCommand}" 
        Style="{StaticResource AcceptAppBarButtonStyle}"
        Tag="Save" />


La expresión "Command={Binding SaveCommand}" crea un enlace entre la propiedad Command del botón y la propiedad SaveCommand de la clase RotateImageViewModel. La propiedad SaveCommand contiene un identificador para un objeto ICommand. El siguiente código procede del archivo RotateImageViewModel.cpp.


ICommand^ RotateImageViewModel::SaveCommand::get()
{
    return m_saveCommand;
}


La variable de miembro m_saveCommand se inicializa en el constructor de la clase RotateImageViewModel. Este es el código del archivo RotateImageViewModel.cpp.


m_saveCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &RotateImageViewModel::SaveImage), nullptr);


La clase DelegateCommand de Hilo es una implementación de la interfaz ICommand. La clase define el tipo de delegado ExecuteDelegate. Los delegados permiten utilizar un puntero a una función de miembro de C++ como un objeto invocable de Windows en tiempo de ejecución. Hilo invoca el ExecuteDelegate cuando la interfaz de usuario desencadena el comando.

Nota  Para obtener más información sobre la extensión del lenguaje de delegados en C++/CX, consulte Delegados (C++/CX).

Debido a que usamos enlace de datos, para cambiar la acción del comando guardar solo hay que asignar un objeto DelegateCommand diferente a la variable de miembro m_saveCommand. No es necesario cambiar el archivo XAML de la vista.

En este ejemplo, la función de miembro subyacente procede del archivo RotateImageViewModel.cpp.


void RotateImageViewModel::SaveImage(Object^ parameter)
{
   // Asynchronously save image file
}	  

Usar un objeto ubicador de modelo de vista para enlazar vistas a modelos de vista

En Hilo, cada vista (página) tiene su correspondiente modelo de vista.

Si usas MVVM, la aplicación necesita conectar sus vistas con sus modelos de vistas. Esto significa que cada vista debe tener un modelo de vista asignado a su propiedad DataContext. En Hilo, hemos usado una única clase ViewModelLocator porque necesitábamos que el código de instalación se ejecutara antes de enlazar los elementos de la interfaz de usuario al modelo de vista. La clase ViewModelLocator tiene propiedades que recuperan un objeto de modelo de vista para cada página de la aplicación. Consulta Crear páginas y navegar por ellas para ver cómo la clase ViewModelLocator enlaza las vistas y los modelos de vista en Hilo.

No tienes que usar una clase ubicadora de modelo de vista. De hecho, hay varias maneras de enlazar una vista a su correspondiente objeto de modelo de vista. Si no usas una clase ubicadora de modelo de vista, puedes asociar la creación y destrucción de las instancias del modelo de vista con la duración del objeto de vista correspondiente. Por ejemplo, podrías crear una nueva instancia del modelo de vista cada vez que se cargue la página.

También puedes asociar las vistas con los modelos de vista en un archivo de código subyacente. El código de un archivo de código subyacente puede crear una nueva instancia del modelo de vista y asignarla a la propiedad DataContext de la vista. Las instancias del modelo de vista se pueden crear en el método Initialize de la página o en su método OnNavigatedTo.

[Arriba]

Sugerencias para diseñar aplicaciones de la Tienda Windows usando MVVM

Estas son algunas sugerencias para aplicar el patrón MVVM a las aplicaciones de la Tienda Windows en C++.

Mantén las dependencias de la vista fuera del modelo de vista

A la hora de diseñar una aplicación de la Tienda Windows con el patrón MVVM, debes decidir qué colocas en el modelo, que colocas en las vistas y qué colocas en los modelos de vista. Esta división suele ser una cuestión de gusto pero se aplican algunos principios generales. Lo ideal es que definas la vista con XAML, únicamente con código subyacente que no contenga lógica empresarial. También recomendamos que mantengas el modelo de vista libre de dependencias con los tipos de datos de las vistas o elementos de la interfaz de usuario. No incluyas archivos de encabezado de vista en los archivos fuente del modelo de vista.

Centraliza las conversiones de datos en el modelo de vista o en un nivel de conversión

El modelo de vista proporciona los datos del modelo con un formato que la vista pueda usar fácilmente. Para ello, el modelo de vista algunas veces tiene que realizar una conversión de datos. Es buena idea realizar esta conversión de datos en el modelo de vista porque proporciona propiedades con un formato al que la interfaz de usuario puede enlazar.

También se puede tener un nivel de conversión de datos diferente situado entre el modelo de vista y la vista. Esto podría ocurrir, por ejemplo, cuando los tipos de datos necesitan un formato de datos especial que el modelo de vista no proporciona.

Expón los modos operativos en el modelo de vista

El modelo de vista también puede ser responsable de definir los cambios de estado lógico que afectan a algunos aspectos de la presentación de la vista, como una indicación de que hay una operación pendiente o si hay un determinado comando disponible. No necesitas código subyacente para habilitar o deshabilitar elementos de la interfaz de usuario; puedes hacerlo enlazando a una propiedad del modelo de vista.

Observa el siguiente ejemplo.


<Grid Background="{Binding HasPhotos, Converter={StaticResource BrushConverter}}"
      Height="150"
      IsTapEnabled="{Binding HasPhotos}"
      PointerEntered="OnZoomedOutGridPointerEntered"
      Margin="0"
      Width="150">


En el ejemplo, el atributo IsTapEnabled se enlaza a la propiedad HasPhoto del modelo de vista.

Asegúrate de que los modelos de vista tienen el atributo Bindable

Para que el modelo de vista participe en el enlace de datos con la vista, las clases del modelo de vista deben tener el atributo Windows::UI::Xaml::Data::Bindable para garantizar que el tipo se incluya en el archivo generado por el código XAML.

Además, necesitas incluir el encabezado para tu modelo de vista en el archivo de encabezado App.xaml.h, directa o indirectamente. En Hilo, todos los archivos de encabezado de los modelos de vista están incluidos en el archivo ViewModelLocator.h, que se incluye en el archivo App.xaml.h. Así nos aseguramos de que los tipos que se necesitan para trabajar con XAML se generen correctamente en tiempo de compilación.

Nota  Para obtener más información sobre la extensión del lenguaje de atributos de C++/CX, consulta Atributos definidos por el usuario (C++/CX).

Este es un ejemplo del atributo Bindable.


[Windows::UI::Xaml::Data::Bindable] 
[Windows::Foundation::Metadata::WebHostHiddenAttribute]
public ref class MainHubViewModel sealed : public ViewModelBase 
{ 
  // ...  
} 	  

Asegúrate de que los modelos de vista implementan la interfaz INotifyProperyChanged para que el enlace de datos funcione

Los modelos de vista que necesitan notificar a los clientes que un valor de propiedad ha cambiado deben desencadenar el evento PropertyChanged. Para ello, las clases del modelo de vista deben implementar la interfaz Windows::UI::Xaml::Data::INotifyPropertyChanged. Windows en tiempo de ejecución registra un controlador para este evento cuando la aplicación se ejecuta. Visual Studio proporciona una implementación de la interfaz INotifyPropertyChanged en la clase de plantilla BindableBase, que puedes usar como clase base para cualquier origen de datos XAML. Este es el archivo de encabezado generado, de BindableBase.h:



[Windows::Foundation::Metadata::WebHostHidden]
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
{
  public:
    virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;

   // ...

  protected:
    virtual void OnPropertyChanged(Platform::String^ propertyName);
};	  

La implementación generada invoca el controlador cuando se desencadena el evento.


void BindableBase::OnPropertyChanged(String^ propertyName)
{
    PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}	  

Nota  C++/CX tiene eventos y propiedades como parte del lenguaje de programación. Incluye las palabras clave de eventos y propiedades. Los tipos de Windows en tiempo de ejecución declaran eventos en sus interfaces públicas a las que tu aplicación se puede suscribir. El suscriptor realiza acciones personalizadas cuando el editor desencadena el evento. Para obtener más información acerca de las características de C++/CX compatibles con Windows en tiempo de ejecución, consulta Creación de componentes de Windows en tiempo de ejecución en C++. Para obtener más información acerca de la extensión del lenguaje de eventos de C++/CX que se usa en este ejemplo de código, consulta Eventos (C++/CX).

Las clases del modelo de vista pueden heredar la implementación INotifyPropertyChanged derivando de la clase BindableBase. Por ejemplo, esta es la declaración de la clase ViewModelBase en Hilo. El código procede del archivo ViewModelBase.h.


public ref class ViewModelBase : public Common::BindableBase
{
  // ...
}	  

Siempre que los modelos de vista necesitan indicar a la interfaz de usuario que una propiedad enlazada ha cambiado, llaman al método OnPropertyChanged que heredan de la clase BindableBase. Por ejemplo, este es un método de establecimiento de propiedades definido en la clase RotateImageViewModel.


void RotateImageViewModel::RotationAngle::set(float64 value)
{
    m_rotationAngle = value;

    // Derive margin so that rotated image is always fully shown on screen.
    Thickness margin(0.0);
    switch (safe_cast<unsigned int>(m_rotationAngle))
    {
    case 90:
    case 270:
        margin.Top = 110.0;
        margin.Bottom = 110.0;
        break;
    }
    m_imageMargin = margin;
    OnPropertyChanged("ImageMargin");
    OnPropertyChanged("RotationAngle");
}


Nota  Las notificaciones sobre propiedades al código XAML debe ocurrir en el subproceso de la interfaz de usuario. Esto significa que el método OnPropertyChanged y cualquiera de sus llamadores también deben ocurrir en el subproceso de interfaz de usuario de la aplicación. En general, una convención útil es que se debe llamar a todos los métodos y propiedades del modelo de vista en el subproceso de interfaz de usuario de la aplicación.

Haz que las vistas y los modelos de vista sean independientes

Si sigues los principios resumidos aquí, podrás volver a implementar un modelo de vista sin realizar cambios en la vista. El enlace de vistas a una propiedad determinada en su origen de datos debe ser la dependencia principal de una vista con su correspondiente modelo de vista. (Si cambias el nombre de una propiedad enlazada en el modelo de vista, tienes que cambiar el nombre también en la expresión del enlace de datos de XAML).

Usa técnicas de programación asincrónica para que la interfaz de usuario mantenga su capacidad de respuesta

El objetivo de las aplicaciones de la Tienda Windows es ofrecer una experiencia de usuario rápida y fluida. Por este motivo, Hilo mantiene el subproceso de interfaz de usuario libre de bloqueos. Hilo usa métodos de la biblioteca asincrónica para las operaciones de E/S y tareas en paralelo cuando las operaciones realizan una cantidad significativa de cálculos. Hilo desencadena eventos para notificar asincrónicamente a la vista de un cambio en una propiedad.

Para obtener más información, consulta Programación asincrónica para aplicaciones de la Tienda Windows con C++ y XAML.

Cumple siempre las reglas de subprocesos para los objetos de Windows en tiempo de ejecución

Algunas veces, los objetos que se crean mediante llamadas a Windows en tiempo de ejecución son de un solo subproceso. Esto significa que debes invocar los métodos, propiedades y controladores de eventos desde el contexto de subproceso que se usó para crear el objeto. En la mayoría de los casos, el contexto es el subproceso de interfaz de usuario de la aplicación.

Para evitar errores, Hilo se diseñó de manera que las llamadas a sus modelos de vista tuvieran lugar en el subproceso de interfaz de usuario de la aplicación. (Las clases del modelo realizan operaciones lentas, como el procesamiento de imágenes en subprocesos de trabajo).

Para obtener más información, consulta Patrones de programación para interfaces de usuario asincrónicas. Consulta Controlar el subproceso de ejecución para obtener información sobre el modelo de subprocesos que usan las aplicaciones de la Tienda Windows.

Para ver un tutorial de cómo usa Hilo la programación asincrónica, consulta programación asincrónica para aplicaciones de la Tienda Windows con C++ y XAML.

[Arriba]

 

 

Mostrar:
© 2014 Microsoft