2013-03-06 7 views
5

Ich entwickle ein Windows Store-Apps-Spiel mit WinRT Caliburn.Micro, und ich verlasse mich auf das Navigations-Framework.Wie Parameter mit WinRT Caliburn.Micro an navigierte Ansichtsmodell übergeben?

Ich habe Modelle für das Spiel Setup (definieren Spieler) und das eigentliche Spiel. Beim Navigieren vom Setup zum Spiel möchte ich die Sammlung von Spielern an das Spielansichtsmodell übergeben. Wie kann ich das machen?

Schematisch, meine Ansicht Modelle sehen zur Zeit wie folgt aus:

public class SetupGameViewModel : NavigationViewModelBase 
{ 
    public SetupGameViewModel(INavigationService ns) : base(ns) { } 

    public IObservableCollection<Player> Players { get; set; } 

    public void StartGame() 
    { 
     // This is as far as I've got... 
     base.NavigationService.NavigateToViewModel<GameViewModel>(); 

     // How can I pass the Players collection from here to the GameViewModel? 
    } 
} 

public class GameViewModel : NavigationViewModelBase 
{ 
    public GameViewModel(INavigationService ns) : base(ns) { } 

    public ScoreBoardViewModel ScoreBoard { get; private set; } 

    public void InitializeScoreBoard(IEnumerable<Player> players) 
    { 
     ScoreBoard = new ScoreBoardViewModel(players); 
    } 
} 

Idealerweise würde ich InitializeScoreBoard aus dem Konstruktor nennen möchte, aber soweit ich in der Lage gewesen, es zu sagen, ist nicht möglich, übergeben Sie die SetupGameViewModel.Players Sammlung an den Konstruktor.

Die INavigationService.NavigateToViewModel<T> (Erweiterung) -Methode nimmt optional ein [object] parameter Argument, aber dieser Parameter scheint den Ansichtsmodellkonstruktor nicht zu erreichen, zu dem navigiert wird. Und ich kann nicht herausfinden, wie man die GameViewModel.InitializeScoreBoard Methode von der SetupGameViewModel.StartGame Methode auch explizit nennt, da die in diesem Stadium nicht initialisiert worden ist.

Antwort

4

Am Ende habe ich das gelöst, indem ich einen temporären Event-Handler implementiert habe. Es stellte sich heraus, dass ich die NavigateToViewModel<T>(object) Überladung verwenden konnte, um die Player-Sammlung zu übergeben.

Aus den Caliburn Micro discussion forum und MSDN documentation habe ich den Eindruck, dass dieser Ansatz nur für "primitive" Typen funktioniert, obwohl ich in meinem Szenario bisher keine Probleme damit festgestellt habe.

Meine SetupGameViewModel.StartGame Methode implementiert ist nun wie folgt:

private static void NavigationServiceOnNavigated(object sender, NavigationEventArgs args) 
{ 
    FrameworkElement view; 
    GameViewModel gameViewModel; 
    if ((view = args.Content as FrameworkElement) == null || 
     (gameViewModel = view.DataContext as GameViewModel) == null) return; 

    gameViewModel.InitializeScoreBoard(args.Parameter as IEnumerable<Player>); 
} 

Nicht wirklich die ich gestrebt hatte saubere Lösung:

public void StartGame() 
{ 
    base.NavigationService.Navigated += NavigationServiceOnNavigated; 
    base.NavigationService.NavigateToViewModel<GameViewModel>(Players); 
    base.NavigationService.Navigated -= NavigationServiceOnNavigated; 
} 

Und die sehr vorübergehend angebracht NavigationServiceOnNavigated Ereignishandler wie folgt umgesetzt wird für, aber zumindest scheint es zu funktionieren.

+1

Danke dafür, es hat mir viel Zeit gespart. – jimpanzer

+0

In diesem Beitrag von Rob selbst erklärt er, dass es aufgrund des navigationsbasierten Systems in Windows Phone, das auf Uri basiert, die komplexe Deserialisierung nicht unterstützt. http://caliburnmicro.codeplex.com/discussions/267562 –

6

OK, setzen es nur gibt, hat Caliburn.Micro einheitliche Navigation für WP8 und WinRT:

NavigationService.UriFor<TargetViewModel>().WithParam(x => x.TargetProperty, ValueToPass).Navigate(); 

Und Sie können Kette WithParam für mehrere Parameter. Jetzt gibt es einige Einschränkungen, nicht alle Typen gehen durch, ich bin nicht ganz sicher, was der genaue Grund dafür ist, aber es hat etwas zu tun, wie die Navigation in WinRT funktioniert. Es wurde irgendwo in Caliburn.Micro discussion section erwähnt.

Wie auch immer, Sie können auf diese Weise navigieren. Verlassen Sie sich nicht auf Konstruktor, es wird OnInitialize und OnActivate aufrufen. Also, nur um es in dem Beispiel zu schneiden:

NavigationService.UriFor<DetailsViewModel>().WithParam(x => x.Id, SelectedDetailsId).Navigate(); 

dann im DetailsViewModel:

protected override void OnInitialize() 
{ 
    //Here you'll have Id property initialized to 'SelectedDetailsId' from the previous screen. 
} 

Also, in der reinen Theorie, könnten Sie tun:

NavigationService.UriFor<GameViewModel>().WithParam(x => x.Players, Players).Navigate(); 

im Setup und dann:

public class GameViewModel 
{ 
    public GameViewModel(INavigationService ns) : base(ns) 
    { 
     //It would probably be good to initialize Players here to avoid null 
    } 

    public ScoreBoardViewModel ScoreBoard { get; private set; } 

    public IObservableCollection<Player> Players {get;set;} 

    protected void OnInitialize() 
    { 
     //If everything goes as expected, Players should be populated now. 
     ScoreBoard = new ScoreBoard(Players); 
    } 
} 

In p Aber ich glaube nicht, dass die Weitergabe eines komplexen Konstrukts (Sammlung von Klassen usw.) funktionieren wird.

Weitere Urtyp gut funktionieren (int, string, DateTime etc., aber zB URI nicht für mich arbeiten, wurde immer null), so Worst-Case-Szenario/Abhilfe wird zum Beispiel serialisiert die Players listet eine temporäre Datei vor der Navigation auf und übergibt den Dateipfad als Zeichenfolge, um in deserialize.

Es gibt Leute, die mehr in den Rahmen involviert sind, der die SO durchstreift, sie könnten Ihnen wertvollere Einblicke geben.

+0

Vielen Dank, diese äußerst nützliche Informationen war, habe ich eine Menge von dieser Antwort gelernt:

public class SetupGameViewModel : Screen { private readonly INavigationService _navigationService; public MainPageViewModel(INavigationService navigationService) { _navigationService = navigationService; } public IObservableCollection<Player> Players { get; set; } public void StartGame() { _navigationService.NavigateToViewModel<GameViewModel>(Players); } ... } public class GameViewModel : Screen { private IObservableCollection<Player> _parameter; public IObservableCollection<Player> Parameter { get { return _parameter; } set { if (value.Equals(_parameter)) return; _parameter = value; NotifyOfPropertyChange(() => Parameter); } } protected override void OnActivate() { // do something with the player list // ... } ... } 

Ausführlichere Informationen zu diesem Thema finden Sie hier. Wie Sie jedoch vermuten, scheint die 'Players'-Sammlung nicht über' WithParam' passierbar zu sein. Ich habe derzeit eine sehr umständliche Lösung implementiert, wo ich einen lokalen Handler für das Ereignis "Navigated" definiere. Die Ereignisargumente geben den Navigationsinhalt, und ich nehme implizit an, dass der "DataContext" des Inhalts mein Ansichtsmodell ist. Trotzdem wäre es gut, wenn jemand eine zuverlässigere Lösung für dieses Problem hätte. –

+1

BTW, ich denke, ich fand die Diskussion, auf die Sie sich beziehen, [hier] (http://caliburnmicro.codeplex.com/discussions/393238). –

+0

@AndersGustafsson Ja, das ist genau diese Diskussion. Wie ich schon sagte, ich kenne im Moment keine bessere Lösung mit Caliburn.Micro, abgesehen davon, die Daten irgendwo zu speichern und sie dann aus einer Art Schlüssel zu erstellen; von der besagten Diskussion - [sogar Microsoft rät davon ab] (http://msdn.microsoft.com/en-us/library/windows/apps/hh702394.aspx), aber es wird im Allgemeinen * unterstützt * ... –

2

In Win Store Apps können Sie mit Hilfe des NavigationService komplexe Objekte zwischen ViewModels übergeben. Nur in Silverlight Apps sind Sie auf Objekte beschränkt, die zu einer Zeichenfolge serialisiert werden müssen. Diese Einschränkung ist in Win Store-Apps nicht vorhanden.

In Ihrem Fall sollte so etwas wie das Folgende funktionieren. In StartGame() wird mit dem NavigationService das GameViewModel aufgerufen. Die Spielerliste wird als einfacher Parameter übergeben. Per Konvention wird dieser Parameter einem Eigenschaftsparameter des Destination ViewModel zugewiesen. http://wp.qmatteoq.com/using-caliburn-micro-with-universal-windows-app-navigation/

+0

Danke, das ist die Antwort für meine Windows 10 UWP App. Und ja, die Eigenschaft im Zielansichtsmodell ('GameViewModel' im Beispiel) muss' Parameter' genannt werden - scheint die Konvention zu sein. –