在windows store / wp reactiveui应用程序中设置第一个视图/ vm

时间:2014-12-23 01:06:48

标签: windows-store-apps windows-phone-8.1 reactiveui

我无法确定如何使用Reactive UI正确导航到Windows应用商店8.1应用中的第一个视图。

在我的app.xaml.cs中,我有以下内容:

    protected override void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;
        ...

        if (rootFrame.Content == null)
            if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
            {
                throw new Exception("Failed to create initial page");
            }
        }

        // Ensure the current window is active
        Window.Current.Activate();
    }

rootFrame.Navagate正确显示了我的MainPage.xaml视图。该视图声明为:

public sealed partial class MainPage : Page, IViewFor<MainPageViewModel>
{
    public MainPage()
    {
        this.InitializeComponent();

        RxApp.SuspensionHost.ObserveAppState<MainPageViewModel>()
            .BindTo(this, x => x.ViewModel);

        this.BindCommand(ViewModel, x => x.SwitchPages, x => x.FindIndicoUrl);

        //this.NavigationCacheMode = NavigationCacheMode.Required;
    }

但是,ViewModel未自动设置。我错过了什么代码?

接下来,我确定我还有另一个问题:如何从VM导航到新视图。 :-)我假设我应该使用查看第一个导航(来自我读过的其他内容)。

顺便说一下,我查看了文档(在git repo的docs分支中),我担心我无法从那里弄清楚。另外,看看&#34;游乐场......&#34;应用程序,但它们没有构建(由于某些pdb冲突,在那一点上没有推动,因此无法测试)。这些样品似乎也没有使用它,新的样品回购看起来似乎正在进行中。另外,检查SO。

1 个答案:

答案 0 :(得分:4)

在网上狩猎,以及SO(包括我11个月前提出的问题),我拼凑了一个答案,我在下面发帖。如果知道这些东西的人可以告诉我这是否正确,我会喜欢它。

首先,有关于此的文档:https://github.com/reactiveui/ReactiveUI/blob/master/docs/basics/routing.md。但是,对我来说,至少,它太高级了(例如,我没有在WPF世界中使用过RxUI)。但是现在我已经解决了这些问题,它确实有意义。

最后我的目标是看看我是否可以使用通用应用程序的通用代码。以下所有文件都是在共享项目中创建的。为Windows Store应用程序实现后退按钮等时,这可能会有所不同,但我还没有解决这个问题。

首先,您需要设置容器页面。我的MainPage.xaml的XAML如下所示:

<Page
    x:Class="IWalker.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:IWalker"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:rxui="using:ReactiveUI"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <rxui:RoutedViewHost Router="{Binding Router}" />
    </Grid>

主页面cs文件:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        DataContext = Locator.Current.GetService(typeof(IScreen));
    }
}

有很多方法可以设置数据上下文,我不确定最好的方法是什么...但只要你修改你的App.xaml.cs文件看起来像这样的东西就行了构造函数:

    public App()
    {
        this.InitializeComponent();
        this.Suspending += this.OnSuspending;

        autoSuspendHelper = new AutoSuspendHelper(this);
        RxApp.SuspensionHost.CreateNewAppState = () => new MainPageViewModel();
        RxApp.SuspensionHost.SetupDefaultSuspendResume();

        // Register everything... becasue....

        Locator.CurrentMutable.Register(() => new StartPage(), typeof(IViewFor<StartPageViewModel>));
        Locator.CurrentMutable.Register(() => new MeetingPage(), typeof(IViewFor<MeetingPageViewModel>));

        // Create the main view model, and register that.
        var r = new RoutingState();
        Locator.CurrentMutable.RegisterConstant(r, typeof(RoutingState));
        Locator.CurrentMutable.RegisterConstant(new MainPageViewModel(r), typeof(IScreen));
    }

这里的关键是注释“//创建主视图模型”下面的行。你必须创建RoutingState,我可以在那里注册IScreen,以便稍后我可以选择它的单个版本。这是RxUI使用的所有DI代码。我不是100%确定我需要像这样注册的RoutingState,因为我总是最终在IScreen上引用它。所以可能是这条线是不必要的是一个完整的应用程序。

最后,我们需要主页面视图模型:

class MainPageViewModel : ReactiveObject, IScreen
{
    /// <summary>
    /// Return the routing state.
    /// </summary>
    public RoutingState Router { get; private set; }

    public MainPageViewModel(RoutingState state = null)
    {
        Router = state;

        // Go to the first page and get this show ion the road.
        Router.Navigate.Execute(new StartPageViewModel(this));
    }
}

在那里你看到路由器被存储,以及IScreen依赖。而且,最重要的是,您看到切换到StartPage,这是我的应用程序的第一个“有趣的页面”。

起始页非常简单。首先,请注意上面显示的App.xaml.cs ctor中的StartPage注册。接下来,这是xaml供参考:

<Page ...
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox Name="IndicoUrl" MinWidth="200"/>
            <Button Name="FindIndicoUrl" Content="Load It!"/>
        </StackPanel>
    </Grid>
</Page>

接下来是xaml代码背后的代码。请注意,这不仅仅是其他一些框架(如CaliburnMicro)。很可能有更好的方法来做到这一点 - 我知道RxUI支持设计时间创作绑定等,但我还没有解决。我不认为RxUI按惯例实现了连接。

public sealed partial class StartPage : Page, IViewFor<StartPageViewModel>
{
    public StartPage()
    {
        this.InitializeComponent();

        this.BindCommand(ViewModel, x => x.SwitchPages, x => x.FindIndicoUrl);

        this.Bind(ViewModel, x => x.MeetingAddress, y => y.IndicoUrl.Text);
    }

    public StartPageViewModel ViewModel
    {
        get { return (StartPageViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(StartPageViewModel), typeof(StartPage), new PropertyMetadata(null));

    object IViewFor.ViewModel
    {
        get { return ViewModel; }
        set { ViewModel = (StartPageViewModel)value; }
    }
}

请注意顶部的IVeiwFor依赖项。这需要稍微实现ViewModel依赖项属性。请注意,ctor在视图模型上设置绑定,在调用ctor时实际上为null。这是由管道处理的。像往常一样在MVVM中,View知道ViewModel的所有内容,反之亦然。

最后,StartPage视图模型:

public class StartPageViewModel :  ReactiveObject, IRoutableViewModel
{
    /// <summary>
    /// When clicked, it will cause the page to switch and the text to be saved.
    /// </summary>

    public ReactiveCommand<object> SwitchPages { get; set; }

    /// <summary>
    /// The meeting address (bindable).
    /// </summary>
    public string MeetingAddress
    {
        get { return _meetingAddress; }
        set { this.RaiseAndSetIfChanged(ref _meetingAddress, value); }
    }
    private string _meetingAddress;

    /// <summary>
    /// Setup the page
    /// </summary>
    public StartPageViewModel(IScreen screen)
    {
        HostScreen = screen;

        // We can switch pages only when the user has written something into the meeting address text.
        var canNavagateAway = this.WhenAny(x => x.MeetingAddress, x => !string.IsNullOrWhiteSpace(x.Value));
        SwitchPages = ReactiveCommand.Create(canNavagateAway);

        // When we navigate away, we should save the text and go
        SwitchPages
            .Select(x => MeetingAddress)
            .Subscribe(addr =>
            {
                Settings.LastViewedMeeting = addr;
                HostScreen.Router.Navigate.Execute(new MeetingPageViewModel(HostScreen, addr));
            });

        // Setup the first value for the last time we ran.
        MeetingAddress = Settings.LastViewedMeeting;
    }

    /// <summary>
    /// Track the home screen.
    /// </summary>
    public IScreen HostScreen {get; private set;}

    /// <summary>
    /// Where we will be located.
    /// </summary>
    public string UrlPathSegment
    {
        get { return "/home"; }
    }
}

请注意最后两个属性,这些属性是可路由视图接口依赖项所要求的。 HostScreen需要由ctor设置,并且正如您在RxUI应用程序中所期望的那样,您可以在VM的ctor中设置所有行为。

我在这里远非专家。如果有更简单的方法,请告诉我,我会尝试更新此内容。或者删除一些样板代码的方法! : - )