2010-05-20 5 views
5

Próbuję zbudować przykładową grę w Silverlight 4 przy użyciu wzorca projektowego MVVM, aby poszerzyć moją wiedzę. Używam również zestawu narzędzi MvvmLight Laurenta Bugniona (tutaj: http://mvvmlight.codeplex.com/). Wszystko, co chcę teraz zrobić, to przesuwać kształt wokół płótna, naciskając określone klawisze. Moje rozwiązanie zawiera Player.xaml (tylko prostokąt, to zostanie przeniesione) i MainPage.xaml (Canvas i instancja kontrolki Player).Silverlight 4 + MVVM + wydarzenie KeyDown

Silverlight nie obsługuje tunelowania zdarzeń routowanych, a jedynie propagacji. Mój wielki problem polega na tym, że Player.xaml nigdy nie rozpoznaje zdarzenia KeyDown. Zawsze jest on przechwytywany najpierw przez MainPage.xaml i nigdy nie osiąga żadnych kontrolek podrzędnych, ponieważ pęcznieje w górę. Wolałbym, żeby logika przeniesienia Gracza była w klasie PlayerViewModel, ale nie sądzę, aby Gracz wiedział o jakichkolwiek zdarzeniach KeyDown strzelających bez mnie, jawnie przekazujących je z MainPage.

Skończyło się na dodaniu logiki obsługi do klasy MainPageViewModel. Teraz mój problem polega na tym, że MainPageViewModel nie ma wiedzy o Player.xaml, więc nie może przenieść tego obiektu podczas obsługi zdarzeń KeyDown. Sądzę, że jest to oczekiwane, ponieważ ViewModels nie powinien mieć żadnej wiedzy na temat powiązanych widoków.

W nie tak wielu słowach ... czy istnieje sposób, w jaki kontrolka użytkownika tego odtwarzacza na mojej stronie głównej może bezpośrednio akceptować zdarzenia KeyDown? Jeśli nie, jaka jest idealna metoda, aby mój MainPageViewModel mógł komunikować się z kontrolkami podrzędnymi tego widoku? Próbuję zachować kod z plików za kodem tak bardzo, jak to tylko możliwe. Wydaje się, że najlepiej jest umieścić logikę w ViewModels dla łatwości testowania i odłączyć interfejs użytkownika od logiki.

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

Dzięki z góry Jeremy

+0

Czy Twój MainViewModel ma dostęp do twojego obiektu gracza? Jeśli tak, to czy obiekt Player nie mógł mieć metody o nazwie MoveUp(), a następnie w zdarzeniu KeyPressed można po prostu wywołać Player.MoveUp()? W ten sposób logika przemieszczania się odtwarzacza będzie nadal w obiekcie odtwarzacza. – JSprang

+0

Nie, nie ma odniesienia do obiektu odtwarzacza. Nie ma żadnego odniesienia do XAML, tak jak myślałem, że MVVM ma działać. Czy istnieje sposób, w jaki mogę powiązać moją instancję XAML odtwarzacza z zmienną w MainViewModel? – jturinetti

Odpowiedz

3

Zamiast EventTrigger, spróbuj użyj KeyTrigger i ustaw obiekt Source jako LayoutRoot.

Inną opcją (która moim zdaniem jest lepsza) jest umożliwienie ViewModel obsługi pozycji gracza. Na przykład właściwość o nazwie PlayerTop i właściwość o nazwie PlayerLeft. Zwiąż właściwość Canvas.Top i Canvas.Left dla PLayera. Gdy użytkownik naciśnie klawisze, polecenie zostanie wykonane na maszynie wirtualnej, która aktualizuje te właściwości. W ten sposób maszyna wirtualna nie musi wiedzieć, co się porusza ani jak jest przenoszona.

Czy to ma sens?

+0

Dzięki! Skończyło się na używaniu twojej ostatniej sugestii. Miałem jednak jeszcze jeden problem ... ponieważ odtwarzacz miał przypisany jego DataContext do swojego PlayerViewModel w swoim XAML na poziomie UserControl, powiązania Canvas.Top i Canvas. Player w MainPage.xaml nie mogły znaleźć właściwości PlayerTop/PlayerLeft . Musiałem przenieść powiązanie DataContext gracza do jego kontrolki root Grid. – jturinetti