2010-05-20 13 views
5

Estoy intentando crear un juego de muestra en Silverlight 4 usando el patrón de diseño de MVVM para ampliar mi conocimiento. Estoy usando el kit de herramientas MvvmLight de Laurent Bugnion también (se encuentra aquí: http://mvvmlight.codeplex.com/). Todo lo que quiero hacer ahora es mover una forma dentro de un lienzo presionando teclas específicas. Mi solución contiene un Player.xaml (solo un rectángulo, este se moverá) y MainPage.xaml (el lienzo y una instancia del control del reproductor).Evento Silverlight 4 + MVVM + KeyDown

A mi entender, Silverlight no admite eventos enrutados de túnel, solo burbujeo. Mi gran problema es que Player.xaml nunca reconoce el evento KeyDown. Siempre es interceptado por MainPage.xaml primero y nunca llega a los controles secundarios porque se dispara hacia arriba. Preferiría que la lógica para mover el Reproductor esté en la clase PlayerViewModel, pero no creo que el Jugador pueda saber sobre ningún evento KeyDown que se active sin que yo los pase explícitamente desde MainPage.

Terminé agregando la lógica del controlador a la clase MainPageViewModel. Ahora mi problema es que MainPageViewModel no tiene conocimiento de Player.xaml por lo que no puede mover este objeto cuando maneja eventos KeyDown. Supongo que esto se espera, ya que ViewModels no debería tener ningún conocimiento de sus Vistas asociadas.

En pocas palabras ... ¿hay alguna forma de que este control de usuario Player en mi MainPage.xaml acepte y maneje directamente eventos KeyDown? Si no, ¿cuál es el método ideal para que mi MainPageViewModel se comunique con los controles secundarios de su Vista? Estoy tratando de mantener el código fuera de los archivos de código subyacente tanto como sea posible. Parece que es mejor poner lógica en ViewModels para facilitar la prueba y desacoplar UI de la lógica.

(MainPage.xaml)

<UserControl x:Class="MvvmSampleGame.MainPage" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:game="clr-namespace:MvvmSampleGame"    
     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
     xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4" 
     mc:Ignorable="d" 
     Height="300" 
     Width="300" 
     DataContext="{Binding Main, Source={StaticResource Locator}}"> 

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="KeyDown"> 
     <cmd:EventToCommand Command="{Binding KeyPressCommand}" PassEventArgsToCommand="True" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

<Canvas x:Name="LayoutRoot">  
    <game:Player x:Name="Player1"></game:Player> 
</Canvas> 

(MainViewModel.cs)

public MainViewModel() 
{ 

    KeyPressCommand = new RelayCommand<KeyEventArgs>(KeyPressed); 

}  

public RelayCommand<KeyEventArgs> KeyPressCommand 
{ 
    get; 
    private set; 
} 

private void KeyPressed(KeyEventArgs e) 
{ 

     if (e.Key == Key.Up || e.Key == Key.W) 
     { 
      // move player up 

     } 
     else if (e.Key == Key.Left || e.Key == Key.A) 
     { 
      // move player left 
     } 
     else if (e.Key == Key.Down || e.Key == Key.S) 
     { 
      // move player down 
     } 
     else if (e.Key == Key.Right || e.Key == Key.D) 
     { 
      // move player right 
     } 
} 

Gracias de antemano, Jeremy

+0

¿Su MainViewModel tiene acceso a su objeto de jugador? Si es así, ¿no podría el objeto Player tener un método llamado MoveUp(), entonces en el evento KeyPressed podría simplemente llamar a Player.MoveUp()? De esta manera, la lógica para el movimiento del jugador aún estaría en el objeto Player. – JSprang

+0

No, no tiene una referencia al objeto Player. No tiene ninguna referencia al XAML en absoluto, que es como pensé que se suponía que MVVM funcionara. ¿Hay alguna manera de vincular mi instancia XAML del reproductor a una variable en MainViewModel? – jturinetti

Respuesta

3

lugar de utilizar el EventTrigger, tratar de use el KeyTrigger y configure el objeto Source para que sea el LayoutRoot.

Otra opción (que creo que es mejor) es dejar que el ViewModel maneje la posición del jugador. Por ejemplo, tiene una propiedad llamada PlayerTop y una propiedad llamada PlayerLeft. Enlaza la propiedad Canvas de Top.Top y Canvas.Left con estos. Cuando el usuario presiona las teclas, se ejecuta un comando en la máquina virtual que actualiza estas propiedades. De esta forma, la VM no tiene que saber qué se mueve ni cómo se mueve.

¿Tiene sentido?

+0

Gracias! Terminé usando tu última sugerencia. Sin embargo, tenía un problema más ... porque el reproductor tenía su DataContext ligado a su PlayerViewModel en su XAML en el nivel de UserControl, los enlaces Canvas.Top y Canvas.Left del jugador en MainPage.xaml no podían encontrar las propiedades PlayerTop/PlayerLeft . Tuve que mover el enlace DataContext del jugador a su control de raíz de cuadrícula. – jturinetti

Cuestiones relacionadas