2012-09-07 26 views
10

Estoy trabajando en hacer mi primer juego usando C# y XAML para Windows 8. Todavía estoy aprendiendo los conceptos básicos y las mejores prácticas, y MVVM ha sido un obstáculo. Intentaré hacer la pregunta en dos partes.Jerarquía MVVM y View/ViewModel

Antecedentes

El juego que estoy haciendo es Sudoku. Sudoku tiene una tabla que contiene una cuadrícula de mosaicos de 9x9. Tengo tres modelos: Game, Board y Tile. Cuando se crea un Game, crea automáticamente un Board, y cuando se crea el Board, crea 81 (9x9) .

1. ¿Con una jerarquía de vistas, cómo se crean los modelos de vista correspondientes?

para que coincida con la jerarquía de los modelos, me gustaría tener una jerarquía de puntos de vista (GameView contiene una BoardView que contiene 81 TileViews). En XAML, es bastante fácil crear esta jerarquía de vistas con controles de usuario, pero no entiendo cómo se crean los modelos de vista.

En los ejemplos que he visto, el contexto de datos de un control de usuario a menudo se establece en el modelo de vista (usando el ViewModelLocator como fuente) que crea una nueva instancia del modelo de vista. Esto parece funcionar bien si tienes una vista plana, pero también parece que se complica cuando tienes una jerarquía. ¿El GameView crea un y lo deja en manos de su hijo BoardView para crear un BoardViewModel? Si es así, ¿cómo se comunica el con el BoardViewModel? ¿Puede el BoardViewModel comunicarse una copia de seguridad de la jerarquía al ?

2. ¿Cómo obtiene un modelo de vista los datos del modelo?

En iOS, comenzaría utilizando un servicio para buscar un modelo Game que se rellenó previamente con datos. Luego crearía un controlador de vista GameViewController (que estaba a cargo de crear la vista) y le pasaría el Game. En MVVM, veo el valor de tener una vista a cargo de crear su propio modelo de vista (idealmente usando un ViewModelLocator), pero no entiendo cómo ese modelo de vista obtiene el modelo.

En todos los ejemplos que he encontrado en línea, el modelo de vista utiliza algún servicio para recuperar sus propios datos. Pero no he encontrado ningún ejemplo que acepte parametros de constructor o params pasados ​​desde un nivel superior de navegación. ¿Cómo se hace esto?

No quiero usar un recurso de aplicación u otro tipo de método de almacenamiento singleton para mi modelo porque, no es lo que hago, pero ¿qué ocurre si quiero mostrar varios acertijos en la pantalla a la vez? Cada GameView debe contener su propio Game.

No sólo el necesitan una referencia al modelo de Game, pero el BoardViewModel que se creó alguna manera (ver pregunta 1) necesita una referencia al modelo Board que pertenece al modelo Game. Lo mismo ocurre con todos los . ¿Cómo se transmite toda esta información en la cadena? ¿Puedo hacer este trabajo mucho más pesado completamente dentro de XAML, o voy a tener que hacer algún tipo de encuadernación u otra inicialización en el código?

¡Uf!

Agradezco cualquier consejo que pueda dar, incluso si no es una respuesta completa. También estoy interesado en encontrar ejemplos de proyectos de MVVM que compartan desafíos similares a los míos. ¡Gracias una tonelada!

+0

Creo que está hablando del problema de controles de usuario anidados (vea http://catel.catenalogic.com/index.htm?gs_viewscontrols_nested_user_controls_problem.htm). Catel en sí no está disponible para WinRT todavía (una versión beta es sin embargo), pero al menos puede entender cómo creo que debería hacerse. –

Respuesta

14

Empezaría creando una clase para comenzar la aplicación. Normalmente llamo esa clase algo así como ApplicationViewModel o ShellViewModel, a pesar de que técnicamente se puede cumplir con diferentes normas que lo que yo usaría normalmente para un ViewModel

Esta clase se crea una instancia en el arranque, y es el DataContext para la ShellView o ApplicationView

// App.xaml.cs 
private void OnStartup(object sender, StartupEventArgs e) 
{ 
    var shellVM = new ShellViewModel(); 
    var shellView = new ShellView();  
    shellView.DataContext = shellVM; 
    shellView.Show(); 
} 

Normalmente, este es el único lugar donde establezco un DataContext para un componente de IU directamente. A partir de este momento, sus ViewModels son la aplicación. Es importante tener esto en cuenta cuando se trabaja con MVVM. Sus Vistas son simplemente una interfaz fácil de usar que permite a los usuarios interactuar con ViewModels. En realidad, no se los considera parte del código de la aplicación.

Por ejemplo, su ShellViewModel puede contener:

  • BoardViewModel CurrentBoard
  • UserViewModel CurrentUser
  • ICommand NewGameCommand
  • ICommand ExitCommand

y su ShellView podría contener algo como esto:

<DockPanel> 
    <Button Command="{Binding NewGameCommand}" 
      Content="New Game" DockPanel.Dock="Top" /> 
    <ContentControl Content="{Binding CurrentBoard}" /> 
</DockPanel> 

En realidad, esto va a hacer que su objeto BoardViewModel en la interfaz de usuario como el ContentControl.Content. Para especificar cómo dibujar su BoardViewModel, puede especificar DataTemplate en ContentControl.ContentTemplate, o utilizar implícitamente DataTemplates.

Un DataTemplate implícito es simplemente un DataTemplate para una clase que no tiene asociado un x:Key. WPF utilizará esta plantilla cada vez que encuentre un objeto de la clase especificada en la interfaz de usuario.

Así, utilizando

<Window.Resources> 
    <DataTemplate DataType="{x:Type local:BoardViewModel}"> 
     <local:BoardView /> 
    </DataTemplate> 
</Window.Resources> 

que significa que en lugar de dibujar

<ContentControl> 
    BoardViewModel 
</ContentControl> 

dibujará

<ContentControl> 
    <local:BoardView /> 
</ContentControl> 

Ahora el BoardView podría contener algo así como

<ItemsControl ItemsSource="{Binding Squares}"> 
    <ItemsControl.ItemTemplate> 
     <ItemsPanelTemplate> 
      <UniformGrid Rows="3" Columns="3" /> 
     </ItemsPanelTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

y dibujaría una placa usando un 3x3 UniformGrid, con cada celda que contiene el contenido de su matriz Squares.Si su propiedad BoardViewModel.Squares pasó a ser una serie de TileModel objetos, cada celda de la cuadrícula contendría una TileModel, y que podría volver a usar una implícita DataTemplate para contar WPF cómo dibujar cada TileModel

Ahora en cuanto a cómo su ViewModel obtiene su objetos de datos reales, eso depende de usted. Prefiero abstraer todo el acceso a datos detrás de una clase como Repository, y hacer que mi ViewModel simplemente llame a algo como SodokuRepository.GetSavedGame(gameId);. Hace que la aplicación sea fácil de probar y mantener.

Sin embargo, como obtienes tus datos, ten en cuenta que ViewModel y Models son tu aplicación, por lo que deben ser responsables de obtener los datos. No hagas eso en el View. Personalmente, me gusta mantener mi capa Model para objetos simples que solo contienen datos, por lo que solo realizo operaciones de acceso a datos desde mis ViewModels.

Para la comunicación entre ViewModels, en realidad tengo un article on my blog sobre eso. Para resumir, utilice un sistema de mensajería como Microsoft Prism EventAggregator o MVVM Light Messenger. Funcionan como un tipo de sistema de paginación: cualquier clase puede suscribirse para recibir mensajes de un tipo específico, y cualquier clase puede transmitir mensajes.

Por ejemplo, su ShellViewModel puede suscribirse para recibir mensajes ExitProgram y cerrar la aplicación cuando escucha uno, y puede transmitir un mensaje ExitProgram desde cualquier lugar de su aplicación.

Supongo que otro método sería simplemente adjuntar controladores de una clase a otra, como llamar al CurrentBoardViewModel.ExitCommand += Exit; desde el ShellViewModel, pero me parece complicado y prefiero usar un sistema de mensajería.

De todos modos, espero que responda algunas de sus preguntas y lo guiará en la dirección correcta. Goodluck con su proyecto :)

+0

¡Guau, eso es mucho para digerir! Permítanme resumir para ver si lo tengo: las vistas principales contendrán ContentTemplates con el atributo de contenido establecido para ver los objetos del modelo. DataTemplates define esas ContentTemplates para contener las vistas correspondientes. Esto significa que no está usando ViewModelLocator o IoC, ¿correcto? –

+0

Además, el acceso a los datos todavía me preocupa. Si el modelo de vista es responsable de obtener su propio modelo, ¿hay alguna manera de que pueda especificar información adicional sobre qué modelo usar? Si tengo rompecabezas difíciles y rompecabezas fáciles, ¿cómo puedo decirle a mi nuevo GameViewModel qué rompecabezas utilizar? –

+2

@BrentTraut Personalmente, no me gusta usar ViewModelLocator. Existen algunas restricciones, como que solo puede tener una instancia de su ViewModel y no poder pasar los parámetros. Prefiero construir mi aplicación en código y hacer que la interfaz de usuario simplemente proporcione una interfaz fácil de usar para mis clases. – Rachel