Silverlight 4 + MVVM + KeyDown事件

问题描述:

我想在Silverlight 4中使用MVVM设计模式来扩展我的知识来构建一个示例游戏。我也使用Laurent Bugnion的MvvmLight工具包(在这里找到:http://mvvmlight.codeplex.com/)。我现在想要做的就是通过按特定键在Canvas中移动一个形状。我的解决方案包含一个Player.xaml(只是一个矩形;这将被移动)和MainPage.xaml(Canvas和Player控件的一个实例)。Silverlight 4 + MVVM + KeyDown事件

据我的理解,Silverlight不支持隧道路由事件,只冒泡。我的大问题是Player.xaml永远不会识别KeyDown事件。它总是首先被MainPage.xaml拦截,它永远不会到达任何子控件,因为它向上冒泡。我更喜欢移动播放器的逻辑是在PlayerViewModel类中,但我不认为播放器可以知道任何KeyDown事件没有我明确地从MainPage传递它们。

我最终将处理程序逻辑添加到MainPageViewModel类。现在我的问题是,MainPageViewModel不知道Player.xaml,所以在处理KeyDown事件时它不能移动这个对象。我想这是预期的,因为ViewModels不应该有任何关于它们相关视图的知识。

没有那么多的话......这个Player用户在我的MainPage.xaml中控制的方式是否可以直接接受和处理KeyDown事件?如果不是,我的MainPageViewModel与View的子控件进行通信的理想方法是什么?我试图尽可能避免代码隐藏文件中的代码。似乎最好将逻辑放在ViewModel中以便于测试并将UI与逻辑分离。

(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 
     } 
} 

由于提前, 杰里米

+0

您的MainViewModel是否可以访问您的播放器对象?如果是这样,那么Player对象上就不能有一个名为MoveUp()的方法,那么在KeyPressed事件中,你可以调用Player.MoveUp()?这样,播放器移动的逻辑仍然在Player对象中。 – JSprang 2010-05-20 17:23:04

+0

不,它没有对Player对象的引用。它没有提及任何XAML,这是我认为MVVM应该如何工作的。有没有一种方法可以将我的Pl​​ayer的XAML实例绑定到MainViewModel中的变量? – jturinetti 2010-05-21 15:29:34

除了使用EventTrigger,尽量使用KeyTrigger并将Source对象设置为LayoutRoot。

另一种选择(我认为更好)是让ViewModel处理玩家的位置。例如,有一个名为PlayerTop的属性和一个名为PlayerLeft的属性。将PLayer的Canvas.Top和Canvas.Left属性绑定到这些属性上。当用户按下按键时,会在更新这些属性的VM上执行命令。这样虚拟机就不必知道什么是移动的,或者它是如何移动的。

这有道理吗?

+0

谢谢!我结束了使用你的后一个建议。我还有一个问题,因为Player在其UserContext级别的XAML中将其DataContext绑定到它的PlayerViewModel,MainPage.xaml中的Player的Canvas.Top和Canvas.Left绑定找不到PlayerTop/PlayerLeft属性。我必须将播放器的DataContext绑定向下移动到其网格根控件。 – jturinetti 2010-05-21 18:51:43