Uno平台Silverlight迁移指南:对话框与错误处理机制详解
uno 项目地址: https://gitcode.com/gh_mirrors/uno/Uno
引言
在Silverlight向Uno平台迁移过程中,对话框和错误处理是需要重点关注的组件。本文将深入探讨如何在Uno平台中实现Silverlight的对话框功能,特别是ChildWindow控件的替代方案,并介绍如何构建健壮的对话框管理和错误处理机制。
对话框基础概念
对话框是模态UI覆盖层,用于提供上下文应用信息。它会阻止与应用程序窗口的交互,直到用户明确关闭它。在Silverlight中常用的ChildWindow控件,在UWP/Uno平台中对应的替代方案是ContentDialog。
ContentDialog是一个灵活的控件,可以包含复选框、超链接、按钮和任何其他XAML内容。它既可以通过代码创建,也可以作为新项目项添加,并支持完整的设计器功能。
ContentDialog的重要限制
关键限制:每个线程同一时间只能打开一个ContentDialog。尝试同时打开两个ContentDialog会抛出异常,即使它们试图在不同的AppWindows中打开。
由于UWP环境支持异步操作,应用程序的不同部分可能同时尝试显示ContentDialog。虽然在开发和测试过程中可能不会出现冲突,但在生产环境中可能会出现意外情况。为防止这种情况,建议使用管理器确保一次只显示一个实例,其他实例按顺序排队显示。
构建对话框管理器
DialogManager类实现
我们创建一个DialogManager类,使用SemaphoreSlim类确保一次只有一个显示函数在执行:
internal static class DialogManager
{
private static readonly SemaphoreSlim _oneAtATimeAsync = new SemaphoreSlim(1, 1);
internal static async Task<T> OneAtATimeAsync<T>(
Func<Task<T>> show,
TimeSpan? timeout,
CancellationToken? token)
{
var to = timeout ?? TimeSpan.FromHours(1);
var tk = token ?? new CancellationToken(false);
if (!await _oneAtATimeAsync.WaitAsync(to, tk))
{
throw new Exception($"{nameof(DialogManager)}.{nameof(OneAtATimeAsync)} has timed out.");
}
try
{
return await show();
}
finally
{
_oneAtATimeAsync.Release();
}
}
}
技术要点解析
- SemaphoreSlim:用于限制可以访问资源的线程数,这里设置为1确保一次只有一个对话框显示
- WaitAsync:异步等待信号量可用,支持超时和取消令牌
- 异常处理:使用try-finally确保信号量总是被释放
- 参数说明:
show
:实际显示对话框的方法timeout
:等待超时时间token
:取消令牌
对话框扩展方法
为简化ContentDialog的使用,我们创建了一系列扩展方法:
internal static class DialogExtensions
{
internal static async Task<ContentDialogResult> ShowOneAtATimeAsync(
this ContentDialog dialog,
TimeSpan? timeout = null,
CancellationToken? token = null)
{
return await DialogManager.OneAtATimeAsync(
async () => await dialog.ShowAsync(),
timeout,
token);
}
// 简化按钮设置的扩展方法
internal static ContentDialog SetPrimaryButton(this ContentDialog dialog, string text)
{
dialog.PrimaryButtonText = text;
dialog.IsPrimaryButtonEnabled = true;
return dialog;
}
// 其他按钮设置方法...
}
使用示例
var errorDialog = new ContentDialog
{
Title = "错误标题",
Content = "错误内容"
}.SetPrimaryButton("确定");
await errorDialog.ShowOneAtATimeAsync();
错误对话框处理
结合上述类和额外辅助工具,我们可以简化错误对话框的显示:
internal static class ErrorDialogHelper
{
internal static async Task ShowErrorAsync(string titleResourceKey, string contentResourceKey)
{
await CoreApplication.MainView.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
async () =>
{
var errorWindow = new ContentDialog
{
Title = GetResource(titleResourceKey),
Content = GetResource(contentResourceKey)
}.SetPrimaryButton("确定");
await errorWindow.ShowOneAtATimeAsync();
});
}
internal static async Task ShowFatalErrorAsync<TErrorPage>(
string titleResourceKey,
string contentResourceKey)
where TErrorPage : Page, new()
{
await CoreApplication.MainView.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
async () =>
{
var errorDialog = new ContentDialog
{
Title = GetResource(titleResourceKey),
Content = GetResource(contentResourceKey)
}.SetPrimaryButton("退出应用");
await errorDialog.ShowOneAtATimeAsync();
// 致命错误处理
#if __WASM__
Window.Current.Content = new TErrorPage();
#else
Application.Current.Exit();
#endif
});
}
}
使用场景分析
- 普通错误:使用
ShowErrorAsync
显示非致命错误 - 致命错误:使用
ShowFatalErrorAsync
显示致命错误,并根据平台执行不同操作:- WASM平台:替换为错误页面
- 其他平台:退出应用
最佳实践建议
- 线程安全:始终通过Dispatcher在主线程上创建和显示对话框
- 资源管理:使用资源键而非硬编码字符串,便于本地化
- 错误分类:区分普通错误和致命错误,采取不同处理策略
- 日志记录:在显示错误对话框前记录错误信息,便于问题追踪
总结
在Uno平台中实现Silverlight的对话框功能需要特别注意线程安全和并发控制。通过构建DialogManager和配套的扩展方法,我们可以创建健壮、易用的对话框系统。错误处理机制则确保了应用在异常情况下仍能提供良好的用户体验。
这种模式不仅适用于从Silverlight迁移的场景,也是任何Uno/UWP应用中处理对话框和错误的良好实践。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考