等待导航完成继续

时间:2014-03-10 10:41:00

标签: c# windows-phone-8 async-await

我的团队一直在努力寻求最佳实践方法来处理导航响应大约3周,而没有明确的答案。我们有WPF和Windows Phone 8解决方案,我们共享一个共同的代码库。

对于Phone 8,我们显示公司的启动画面并开始初始化我们的数据。由于我们的复杂性,我们在应用程序完全运行之前有一长串的初始化步骤。

protected override void OnNavigatedTo(NavigationEventArgs e) 
{
    base.OnNavigatedTo(e);

    if (e.NavigationMode == NavigationMode.New) 
    {
        BeginAppStartup();
        return;
    }

        ....

void BeginAppStartup() 
{
    // Initialization of settings and environment

此时,我们需要选择性地显示最多5个不同的页面,以请求其他数据。所以我们检查我们的命令,如果可执行,那么我们导航并可选地显示通信页面,登录页面或其他几个可能的页面。

    if( condition )
        DisplayLoginPage();

在WPF中,这很容易,因为我们有模态对话框,可以在继续之前等待用户的输入。但是在WP8的异步世界中,我们不再拥有它。

为了适应这个平台,我们已经实现了大量的尝试,包括保存下一个要执行的命令。我认为我们确信页面已关闭的唯一地方是在启动页面的OnNavigatedTo。

protected override void OnNavigatedTo(NavigationEventArgs e) 
{
    base.OnNavigatedTo(e);

    if (e.NavigationMode == NavigationMode.Back) 
    {
        // If we are returning to the splash from another set up page, check if there are new actions to perform

        if (_startupAction != null) 
        {
            _startupAction();
            return;
        }

不幸的是,由于我们的所有操作都在UI线程中,因此登录页面未正确关闭,因此这只是可以接受的。代码继续,但启动页面隐藏在仍然可见的登录页面后面。

我们也试过了AutoResetEvents,但由于我们必须导航出UI线程,我们无法阻止UI线程。我们也尝试过类似问题的Task.Run。

    // Doesn't work.

void ShowLoginPage() 
{
    if (condition) 
    {
        _manualResetEvent.Reset();
        NavigationService.Navigate(new Uri("/Views/Login.xaml", UriKind.Relative)
        _manualResetEvent.WaitOne();
    }
}

我们也尝试过async / await任务,但是我们遇到了类似的问题。我相信这是最好的解决方案,但我们没有比以前更好的运气。

回到这个问题:从启动页面导航,可选择登录页面,然后等待登录页面完全关闭,然后再继续?

这听起来像是一种非常常见的情况,但我感到困惑!谢谢你的回答。

2 个答案:

答案 0 :(得分:0)

在处理此类复杂导航时,您应该求助于创建自己的导航服务。而不是使用NavigationService.Navigate,使用自己的包装器。

如果登录页面位于启动画面(和可选)之后,但在其他页面之前,您可以在导航后从backstack中删除页面。因此,在这种情况下,总是导航到另一个页面,如果是登录页面,您的自定义服务应该删除最后一页。

答案 1 :(得分:0)

提供类似于模态对话框的功能并不困难。我不确定这是一个很棒的UI设计决定,但它肯定可以做到。 This MSDN blog post介绍了如何使用UserControl作为自定义广告系列。它写于2007年,当时没有async/await也没有WP8。

我将展示如何使用Popup控件(WPF和WP8中都存在)和async/await来执行类似的操作。这是功能部分:

private async void OpenExecuted(object sender, ExecutedRoutedEventArgs e)
{
    await ShowPopup(this.firstPopup);
    await ShowPopup(this.secondPopup);
}

每个弹出窗口都可以并且应该与ViewModel数据绑定。

C#(WPF应用):

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace Wpf_22297935
{
    public partial class MainWindow : Window
    {
        // http://stackoverflow.com/q/22297935/1768303

        public MainWindow()
        {
            InitializeComponent();
        }

        EventHandler ProcessClosePopup = delegate { };

        private void CloseExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.ProcessClosePopup(this, EventArgs.Empty);
        }

        // show two popups with modal-like UI flow
        private async void OpenExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            await ShowPopup(this.firstPopup);
            await ShowPopup(this.secondPopup);
        }

        private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        // helpers

        async Task ShowPopup(Popup popup)
        {
            var tcs = new TaskCompletionSource<bool>();

            EventHandler handler = (s, e) => tcs.TrySetResult(true);
            this.ProcessClosePopup += handler;

            try
            {
                EnableControls(false);

                popup.IsEnabled = true;
                popup.IsOpen = true;

                await tcs.Task;
            }
            finally
            {
                EnableControls(true);

                popup.IsOpen = false;
                popup.IsEnabled = false;

                this.ProcessClosePopup -= handler;
            }
        }

        void EnableControls(bool enable)
        {
            // assume the root is a Panel control
            var rootPanel = (Panel)this.Content;

            foreach (var item in rootPanel.Children.Cast<UIElement>())
                item.IsEnabled = enable;
        }
    }
}

<强> XAML:

<Window x:Class="Wpf_22297935.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Open" CanExecute="CanExecute" Executed="OpenExecuted" />
        <CommandBinding Command="ApplicationCommands.Close" CanExecute="CanExecute" Executed="CloseExecuted"/>
    </Window.CommandBindings>

    <DockPanel>
        <Border Padding="5">
            <StackPanel>

                <StackPanel>
                    <TextBlock>Main:</TextBlock>
                    <TextBox Height="20"></TextBox>
                    <Button Command="ApplicationCommands.Open" HorizontalAlignment="Left" Width="50">Open</Button>
                </StackPanel>

                <Popup Name="firstPopup" AllowsTransparency="true" Placement="Center">
                    <Border Background="DarkCyan" Padding="5">
                        <StackPanel Background="DarkCyan" Width="200" Height="200" HorizontalAlignment="Left">
                            <TextBlock>First:</TextBlock>
                            <TextBox Height="20"></TextBox>
                            <Button Command="ApplicationCommands.Close" HorizontalAlignment="Left" Width="50">Close</Button>
                        </StackPanel>
                    </Border>
                </Popup>

                <Popup Name="secondPopup" AllowsTransparency="true" Placement="Center">
                    <Border Background="DarkGray" Padding="5">
                        <StackPanel Background="DarkGray" Width="200" Height="200" HorizontalAlignment="Left">
                            <TextBlock>Second:</TextBlock>
                            <TextBox Height="20"></TextBox>
                            <Button Command="ApplicationCommands.Close" HorizontalAlignment="Left" Width="50">Close</Button>
                        </StackPanel>
                    </Border>
                </Popup>

            </StackPanel>
        </Border>
    </DockPanel>
</Window>