【问题标题】:Silverlight 4 + MVVM + KeyDown eventSilverlight 4 + MVVM + KeyDown 事件
【发布时间】:2011-02-21 23:55:54
【问题描述】:

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

据我了解,Silverlight 不支持隧道路由事件,仅支持冒泡。我的大问题是 Player.xaml 永远无法识别 KeyDown 事件。它总是首先被 MainPage.xaml 拦截,并且它永远不会到达任何子控件,因为它会向上冒泡。我希望将 Player 移动的逻辑位于 PlayerViewModel 类中,但我认为如果没有我从 MainPage 明确传递它们,Player 无法知道任何触发的 KeyDown 事件。

我最终将处理程序逻辑添加到 MainPageViewModel 类。现在我的问题是 MainPageViewModel 不了解 Player.xaml,因此在处理 KeyDown 事件时它无法移动此对象。我想这是意料之中的,因为 ViewModel 不应该知道它们关联的 View。

话不多说……有没有办法让我的 MainPage.xaml 中的 Player 用户控件直接接受和处理 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
        }
}

提前致谢, 杰里米

【问题讨论】:

  • 您的 MainViewModel 是否有权访问您的播放器对象?如果是这样,难道 Player 对象上不能有一个名为 MoveUp() 的方法,那么在 KeyPressed 事件中你可以只调用 Player.MoveUp() 吗?这样一来,Player 的移动逻辑仍然在 Player 对象中。
  • 不,它没有对 Player 对象的引用。它没有任何对 XAML 的引用,这就是我认为 MVVM 应该如何工作的方式。有没有办法可以将 Player 的 XAML 实例绑定到 MainViewModel 中的变量?

标签: .net silverlight mvvm silverlight-4.0 mvvm-light


【解决方案1】:

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

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

这有意义吗?

【讨论】:

  • 谢谢!我最终使用了你的后一个建议。不过我还有一个问题...因为 Player 在 UserControl 级别的 XAML 中将其 DataContext 绑定到其 PlayerViewModel,所以 MainPage.xaml 中的 Player 的 Canvas.Top 和 Canvas.Left 绑定找不到 PlayerTop/PlayerLeft 属性.我不得不将 Player 的 DataContext 绑定向下移动到它的 Grid 根控件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多