【问题标题】:Waiting for Navigation Complete to continue等待导航完成以继续
【发布时间】:2014-03-11 19:37:02
【问题描述】:

大约 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 任务,但我们遇到了类似的问题。我相信这是最好的解决方案,但我们没有比以前更好的运气了。

回到问题:从启动页面导航到登录页面,然后等待登录页面完全关闭再继续的最佳做法是什么? p>

这听起来很常见,但我很困惑!感谢您的回答。

【问题讨论】:

  • 我可能只是创建一个简单的状态机来保存登录状态。
  • 感谢您的评论,但这不是状态问题。它是“你什么时候保证导航窗口关闭”。我希望它是在 Splash 页面上的“OnNavigatedTo”期间,但事实并非如此。登录页面仍然可见。

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


【解决方案1】:

提供类似于模式对话框的功能并不难。我不确定这是否是一个很棒的 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>

【讨论】:

  • 感谢 Noseratio!我真的很感激这一点。它肯定会给我一些关于如何解决这个问题的好主意。我肯定在研究如何“正确”地做到这一点,并且不介意改变一切来做到“应该”如何去做。这是一种常见的场景(显示页面、获取信息、退出页面、响应数据),我对它的复杂性感到困惑
  • @PhillipDavis,弹出窗口是合法的 WP UI,检查this。不过我有点困惑,您的目标是在其中弹出一个带有Page 的弹出窗口吗? IE。带有导航的弹出窗口?
【解决方案2】:

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

如果登录页面在初始屏幕之后(并且可选),但在其他一些之前,您始终可以在导航后从后台删除页面。因此,在这种情况下,您总是向前导航到另一个页面,如果是登录页面,您的自定义服务应该删除最后一页。

【讨论】:

    猜你喜欢
    • 2020-04-15
    • 1970-01-01
    • 2013-04-25
    • 2019-11-01
    • 2021-05-06
    • 2021-05-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多