2012-09-07 14 views
10

Pracuję nad stworzeniem mojej pierwszej gry przy użyciu C# i XAML dla Windows 8. Wciąż uczę się podstawowych pojęć i najlepszych praktyk, a MVVM było przeszkodą. Spróbuję zadać to pytanie w dwóch częściach.MVVM i View/ViewModel hierarchy

Tło

Gra Robię to Sudoku. Sudoku ma planszę zawierającą 9x9 siatki płytek. Mam trzy modele - Game, Board i Tile. Po utworzeniu Game automatycznie tworzy on Board, a po utworzeniu Board tworzy 81 (9x9) .

1. Czy w hierarchii widoków tworzone są odpowiednie modele widoku?

Aby dopasować hierarchii modeli, chciałbym mieć hierarchię widoków (GameView zawiera BoardView który zawiera 81 TileViews). W XAML łatwo jest utworzyć tę hierarchię widoków za pomocą elementów sterujących użytkownika, ale nie rozumiem, w jaki sposób tworzone są modele widoku.

W przykładach, które widziałem, kontekst danych kontrolki użytkownika jest często ustawiony na model widoku (przy użyciu ViewModelLocator jako źródła), który tworzy świeże wystąpienie modelu widoku. Wydaje się to działać dobrze, jeśli masz płaski widok, ale wydaje ci się, że robi się brudny, kiedy masz hierarchię. Czy model GameView tworzy i pozostawia go dziecku BoardView, aby utworzyć BoardViewModel? Jeśli tak, w jaki sposób komunikuje się z urządzeniem ? Czy BoardViewModel może komunikować się z powrotem w hierarchii do ?

2. W jaki sposób model widoku otrzymuje dane modelu?

W iOS, zacznę od używania usługi do pobrania modelu Game, który został wstępnie wypełniony danymi. Chciałbym następnie utworzyć kontroler widoku GameViewController (który był odpowiedzialny za tworzenie widoku) i przekazać do niego Game. W MVVM widzę, że wartość w widoku jest odpowiedzialna za tworzenie własnego modelu widoku (najlepiej przy użyciu ViewModelLocator), ale nie rozumiem, jak ten model widoku otrzymuje model.

We wszystkich przykładach, które znalazłem online, model widoku korzysta z niektórych usług do pobierania własnych danych. Ale nie natknąłem się na żaden przykład, który akceptuje parametry konstruktora lub parametry przekazane z wyższego poziomu nawigacji. Jak to się robi?

Nie chcę używać zasobu aplikacji lub jakiegoś innego rodzaju sposobu przechowywania pojedynczego modelu dla mojego modelu, ponieważ nie to robię, ale co, gdybym chciał wyświetlać jednocześnie wiele zagadek na ekranie? Każdy kod GameView powinien zawierać własny kod Game.

Nie tylko potrzebują odniesienie do modelu Game, ale BoardViewModel że powstał jakiś sposób (patrz pytanie 1) potrzebuje odniesienie do modelu Board należącym do modelu Game. To samo dotyczy wszystkich modeli . W jaki sposób wszystkie te informacje przekazywane są w łańcuchu? Czy mogę wykonać tak dużo podnoszenia całkowicie w XAML, czy też będę musiał wykonać jakieś powiązanie lub inną inicjalizację w kodzie?

Uff!

Doceniam każdą poradę, którą można udzielić, nawet jeśli nie jest to pełna odpowiedź. Chciałbym także znaleźć przykłady projektów MVVM, które mają podobne wyzwania do mojego. Dzięki za tonę!

+0

Myślę, że mówisz o problemie zagnieżdżonych kontroli użytkownika (zobacz http://catel.catenalogic.com/index.htm?gs_viewscontrols_nested_user_controls_problem.htm). 01/Catel nie jest jeszcze dostępny dla WinRT (jest to wersja beta), ale możesz przynajmniej zrozumieć, jak myślę, że należy to zrobić. –

Odpowiedz

14

Zacznę od utworzenia klasy, z której rozpocznie się aplikacja. Zazwyczaj wzywam tej klasy coś podobnego ApplicationViewModel lub ShellViewModel, choć technicznie może przestrzegać różnych zasad niż to, co ja zazwyczaj używają do ViewModel

Ta klasa dostaje instancja przy starcie i jest DataContext dla ShellView lub ApplicationView

// App.xaml.cs 
private void OnStartup(object sender, StartupEventArgs e) 
{ 
    var shellVM = new ShellViewModel(); 
    var shellView = new ShellView();  
    shellView.DataContext = shellVM; 
    shellView.Show(); 
} 

To jest zwykle jedyne miejsce, w którym ustawiam DataContext dla składnika interfejsu użytkownika. Od tego momentu, Twoje ViewModels są aplikacją. Ważne jest, aby o tym pamiętać podczas pracy z MVVM. Widoki są po prostu przyjaznym dla użytkownika interfejsem, który pozwala użytkownikom na interakcję z ViewModels. W rzeczywistości nie są uważane za część kodu aplikacji.

Na przykład, ShellViewModel może zawierać:

  • BoardViewModel CurrentBoard
  • UserViewModel CurrentUser
  • ICommand NewGameCommand
  • ICommand ExitCommand

i twój ShellView może zawierać coś takiego:

<DockPanel> 
    <Button Command="{Binding NewGameCommand}" 
      Content="New Game" DockPanel.Dock="Top" /> 
    <ContentControl Content="{Binding CurrentBoard}" /> 
</DockPanel> 

To rzeczywiście czynią swój obiekt BoardViewModel w interfejsie użytkownika, jak ContentControl.Content. Aby określić, jak narysować swoją BoardViewModel, można określić DataTemplate w ContentControl.ContentTemplate lub użyć niejawnego DataTemplates.

Niejawny DataTemplate jest po prostu DataTemplate dla klasy, która nie ma powiązanego z nią x:Key. WPF będzie korzystać z tego szablonu za każdym razem, gdy napotka obiekt określonej klasy w interfejsie użytkownika.

Więc za pomocą

<Window.Resources> 
    <DataTemplate DataType="{x:Type local:BoardViewModel}"> 
     <local:BoardView /> 
    </DataTemplate> 
</Window.Resources> 

będzie oznaczać, że zamiast rysowania

<ContentControl> 
    BoardViewModel 
</ContentControl> 

będzie czerpać

<ContentControl> 
    <local:BoardView /> 
</ContentControl> 

Teraz BoardView może zawierać coś takiego

<ItemsControl ItemsSource="{Binding Squares}"> 
    <ItemsControl.ItemTemplate> 
     <ItemsPanelTemplate> 
      <UniformGrid Rows="3" Columns="3" /> 
     </ItemsPanelTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

i będzie rysować planszę za pomocą 3x3 UniformGrid, z każdą komórką zawierającą zawartość tablicy Squares.Jeśli nieruchomość BoardViewModel.Squares się być tablicą TileModel obiektów, a następnie każda komórka siatki będzie zawierać TileModel, i można ponownie użyć niejawny DataTemplate powiedzieć WPF jak wyciągnąć każdą TileModel

Teraz jak w jaki sposób ViewModel dostaje swoją rzeczywiste obiekty danych, to zależy od Ciebie. Wolę abstrakcyjny dostęp do danych za klasą, taką jak Repository, a mój ViewModel po prostu wywołać coś takiego jak SodokuRepository.GetSavedGame(gameId);. Dzięki temu aplikacja jest łatwa do przetestowania i konserwacji.

Jeśli jednak otrzymasz swoje dane, pamiętaj, że są to aplikacje ViewModel i Models, więc powinny one być odpowiedzialne za pobieranie danych. Nie rób tego w View. Osobiście lubię przechowywać moją warstwę Model dla zwykłych obiektów, które przechowują tylko dane, więc tylko kiedykolwiek wykonuję operacje dostępu do danych z moich ViewModels.

Do komunikacji między ViewModels, faktycznie mam o tym article on my blog. Podsumowując, użyj systemu przesyłania wiadomości, takiego jak EventAggregator firmy Microsoft Prism lub Messenger MVVM Light. Działają one jak rodzaj systemu przywoławczego: każda klasa może subskrybować otrzymywanie wiadomości określonego typu, a każda klasa może wysyłać wiadomości.

Na przykład Twoja ShellViewModel może zasubskrybować otrzymywanie wiadomości ExitProgram i zamknąć aplikację, gdy usłyszy jeden z nich, i możesz wysyłać wiadomość ExitProgram z dowolnego miejsca w aplikacji.

Przypuszczam inny sposób byłoby po prostu dołączyć koparki z jednej klasy do drugiej, takie jak wywoływanie CurrentBoardViewModel.ExitCommand += Exit; z ShellViewModel, ale uważam, że bałagan i wolą za pomocą systemu wiadomości.

W każdym razie, mam nadzieję, że odpowie na niektóre pytania i wskaże właściwy kierunek. Goodluck z twoim projektem :)

+0

Wow, to dużo do strawienia! Pozwól mi podsumować, aby zobaczyć, czy mam go: Widoki nadrzędne będą zawierać ContentTemplates z atrybutem zawartości zestaw do wyświetlania obiektów modelu. DataTemplates określają zawartość obiektów ContentTemplates tak, aby zawierały odpowiednie widoki. Oznacza to, że nie używasz ViewModelLocator lub IoC, prawda? –

+0

Również dostęp do danych nadal mnie dotyczy. Jeśli model widoku jest odpowiedzialny za pobranie własnego modelu, czy istnieje sposób, w jaki mogę określić dodatkowe informacje o tym, którego modelu użyć? Jeśli mam trudne układanki i łatwe łamigłówki, w jaki sposób mogę powiedzieć mojej nowej GameViewModel, której puzzli użyć? –

+2

@BrentTraut Osobiście nie lubię używać ViewModelLocator. Obowiązują w nim pewne ograniczenia, na przykład możesz mieć tylko jedną instancję swojego ViewModel i nie możesz przekazać jej parametrów. Wolę tworzyć swoją aplikację w kodzie i mieć interfejs użytkownika po prostu zapewniać przyjazny dla użytkownika interfejs dla moich zajęć. – Rachel