如何使用WinRT Caliburn.Micro将参数传递给导航视图模型?

And*_*son 5 c# caliburn.micro windows-runtime windows-store-apps

我正在使用WinRT Caliburn.Micro开发Windows应用商店应用游戏,我依赖于导航框架.

我有游戏设置(定义玩家)和实际游戏的视图模型.当从设置导航到游戏时,我想将玩家的集合传递给游戏视图模型.我怎样才能做到这一点?

原理上,我的视图模型目前看起来像这样:

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

理想情况下,我想InitializeScoreBoardGameViewModel构造函数中调用,但据我所知,它无法将SetupGameViewModel.Players集合传递给GameViewModel构造函数.

INavigationService.NavigateToViewModel<T>(扩展)方法任选地接受一个[object] parameter参数,但该参数似乎没有达到导航到的视图模型构造器.我无法弄清楚如何GameViewModel.InitializeScoreBoardSetupGameViewModel.StartGame方法中显式调用该方法,因为GameViewModel在此阶段尚未初始化.

Pat*_*iek 6

好吧,只是把它放在那里,Caliburn.Micro为WP8和WinRT统一导航:

NavigationService.UriFor<TargetViewModel>().WithParam(x => x.TargetProperty, ValueToPass).Navigate();
Run Code Online (Sandbox Code Playgroud)

您可以链接WithParam多个参数.现在有一些限制,并非所有类型都经过,我不太清楚究竟是什么原因,但它有一些关于导航如何在WinRT中工作的事情.在Caliburn.Micro讨论部分的某处提到了它.

无论如何,你可以这样导航.不要依赖构造函数,它会调用OnInitializeOnActivate.所以,只是把它切成例子:

NavigationService.UriFor<DetailsViewModel>().WithParam(x => x.Id, SelectedDetailsId).Navigate();
Run Code Online (Sandbox Code Playgroud)

然后在DetailsViewModel:

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

所以,在纯理论中,你可以这样做:

NavigationService.UriFor<GameViewModel>().WithParam(x => x.Players, Players).Navigate();
Run Code Online (Sandbox Code Playgroud)

在设置中然后:

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

但在实践中,我并不认为传递像这样的复杂结构(类等集合)会起作用.

更原始类型的工作只是罚款(int,string,DateTime等,但如URI没有工作对我来说,总是null),所以最坏的情况/变通办法,例如,序列化Players的导航和前传列表到临时文件将文件路径作为要反序列化的字符串GameViewModel.

有人更多地参与漫游SO的框架,他们可能会给你更多有价值的见解.


And*_*son 4

最后,我通过实现临时事件处理程序解决了这个问题。事实证明我可以利用NavigateToViewModel<T>(object)重载来传递玩家集合。

Caliburn Micro 讨论论坛MSDN 文档中,我得到的印象是这种方法只能保证适用于“原始”类型,尽管在我的场景中到目前为止我还没有发现它有任何问题。

我的SetupGameViewModel.StartGame方法现在实现如下:

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

临时附加的事件处理程序NavigationServiceOnNavigated的实现如下:

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

这并不是我一直追求的干净解决方案,但至少它看起来有效。