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

时间:2013-03-06 15:49:33

标签: c# windows-runtime windows-store-apps caliburn.micro

我正在使用 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);
    }
}

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

INavigationService.NavigateToViewModel<T>(扩展名)方法可选地采用[object] parameter参数,但此参数似乎无法到达导航到的视图模型构造函数。我无法弄清楚如何从GameViewModel.InitializeScoreBoard方法明确调用SetupGameViewModel.StartGame方法,因为GameViewModel尚未在此阶段初始化。

3 个答案:

答案 0 :(得分:6)

好的,只需将它放在那里,Caliburn.Micro为WP8和WinRT提供统一导航:

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

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

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

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

然后在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);
    }
}

在实践中,我不认为传递像这样的复杂结构(类等的集合)是可行的。

更原始的类型工作得很好(intstringDateTime等等,但例如URI对我不起作用,总是{{1}例如,最坏情况/解决方法是在导航之前将null列表序列化为临时文件,并将文件路径作为字符串传递给Players中的反序列化。

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

答案 1 :(得分:4)

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

Caliburn Micro discussion forumMSDN documentation我得到的印象是这种方法只能保证适用于“原始”类型,尽管在我的方案中我到目前为止还没有发现任何问题。< / p>

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

不是我真正努力的干净解决方案,但至少它似乎有效。

答案 2 :(得分:2)

在Win Store应用程序中,您可以在NavigationService的帮助下在ViewModel之间移交复杂对象。仅在Silverlight应用程序中,您才被限制为必须可序列化为字符串的对象。 Win Store应用程序中不存在此限制。

在您的情况下,类似下面的内容应该有效。在StartGame()中,NavigationService用于调用GameViewModel。播放器列表作为简单参数移交。按照惯例,此参数将分配给目标ViewModel的属性Parameter。

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/