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

本文关键字:参数 导航 视图 模型 何使用 WinRT Caliburn Micro | 更新日期: 2023-09-27 18:02:29

我正在使用WinRT Caliburn开发Windows Store应用程序游戏。微,我依靠导航框架。

我有游戏设置(定义玩家)和实际游戏的视图模型。当从设置导航到游戏时,我想将玩家集合传递给游戏视图模型。我该怎么做呢?

概要地说,我的视图模型目前是这样的:
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);
    }
}

理想情况下,我想从GameViewModel构造函数中调用InitializeScoreBoard,但据我所知,不可能将SetupGameViewModel.Players集合传递给GameViewModel构造函数。

INavigationService.NavigateToViewModel<T>(扩展)方法可选地接受[object] parameter参数,但这个参数似乎没有到达导航到的视图模型构造函数。我也不知道如何从SetupGameViewModel.StartGame方法显式地调用GameViewModel.InitializeScoreBoard方法,因为GameViewModel在这个阶段还没有初始化。

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

好的,就放在那里,Caliburn.Micro有统一的WP8和WinRT导航:

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

可以为多个参数链接WithParam。这里有一些限制,不是所有类型都要通过,我不太确定确切的原因是什么,但它与导航在WinRT中的工作方式有关。在Caliburn.Micro讨论区有提到过。

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

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

then in the DetailsViewModel:

protected override void OnInitialize()
{
    //Here you'll have Id property initialized to 'SelectedDetailsId' from the previous screen.
}
所以,在纯理论中,你可以这样做:
NavigationService.UriFor<GameViewModel>().WithParam(x => x.Players, Players).Navigate();

,然后:

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);
    }
}

在实践中,虽然,我不认为传递一个复杂的结构,如(类的集合等)将工作。

更基本的类型工作得很好(int, string, DateTime等,但例如URI不适合我,总是null),所以最坏的情况/解决方案是,例如,在导航之前将Players列表序列化到临时文件,并将文件路径作为字符串传递到GameViewModel中进行反序列化。

在SO中有更多的人参与到框架中,他们可能会给你更有价值的见解。

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

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

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

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

非常临时附加的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>);
}

这不是我想要的干净的解决方案,但至少它似乎有效。

在Win Store Apps中,你可以在NavigationService的帮助下在ViewModels之间传递复杂的对象。只有在Silverlight应用程序中,你被限制为必须可序列化为字符串的对象。这个限制在Win Store应用程序中不存在。

在您的情况下,应该像下面这样工作。在StartGame()中,NavigationService被用来调用GameViewModel。玩家列表作为一个简单的参数被传递。按照约定,该参数将分配给目标ViewModel的属性参数。

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
        // ...
    }
    ...
}

关于这个主题的更多详细信息可以在这里找到:http://wp.qmatteoq.com/using-caliburn-micro-with-universal-windows-app-navigation/