我的团队一直在努力寻求最佳实践方法来处理导航响应大约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任务,但是我们遇到了类似的问题。我相信这是最好的解决方案,但我们没有比以前更好的运气。
回到这个问题:从启动页面导航,可选择登录页面,然后等待登录页面完全关闭,然后再继续?
这听起来像是一种非常常见的情况,但我感到困惑!谢谢你的回答。
答案 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>