Silverlight 4 + MVVM + KeyDown事件

jtu*_*tti 5 .net silverlight mvvm silverlight-4.0 mvvm-light

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

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

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

用不多的话来说......在我的MainPage.xaml中,这个Player用户控件是否可以直接接受和处理KeyDown事件?如果没有,我的MainPageViewModel与其View的子控件进行通信的理想方法是什么?我试图尽可能地将代码保留在代码隐藏文件之外.似乎最好将逻辑放在ViewModels中以便于测试并将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>
Run Code Online (Sandbox Code Playgroud)

(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
        }
}
Run Code Online (Sandbox Code Playgroud)

先谢谢你,杰里米

LBu*_*ion 3

尝试使用 KeyTrigger 并将 Source 对象设置为 LayoutRoot,而不是使用 EventTrigger。

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

那有意义吗?