UWP中如何禁用关闭按钮避免确认对话框重复弹窗错误
我之前也碰到过一模一样的问题——点击窗口右上角的X按钮弹出退出确认框,结果手快点多了就报错,提示不能同时打开多个ContentDialog。你之前用状态变量isBtnCloseClicked来控制的思路没问题,但要每个窗口都加这个变量确实有点麻烦。刚好UWP原生API里就有合适的解决办法,给你分享两个方案:
方案一:直接禁用关闭按钮(最贴合你的需求)
UWP的ApplicationView类有个IsClosable属性,专门用来控制窗口的关闭按钮能不能点。我们可以在弹出确认对话框的时候把这个属性设为false,等对话框关掉再改回true,这样用户在对话框显示期间根本点不了X按钮,从根源上避免重复触发的问题。
修改后的代码是这样的:
Windows.UI.Core.Preview.SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += async (sender, args) => { args.Handled = true; var currentView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView(); // 显示对话框前禁用关闭按钮 currentView.IsClosable = false; ContentDialog locationPromptDialog = new ContentDialog { Title = "Do you want to exit?", Content = "", CloseButtonText = "No", PrimaryButtonText = "Yes" }; // 对话框关闭时恢复关闭按钮的可用性 locationPromptDialog.Closed += (s, e) => { currentView.IsClosable = true; }; ContentDialogResult result = await locationPromptDialog.ShowAsync(); if (result == ContentDialogResult.Primary) { App.Current.Exit(); } };
这个方案完全不需要额外的状态变量,用UWP原生API就搞定,逻辑也很清晰。
方案二:用异步锁阻止重复创建对话框
如果你不想禁用关闭按钮,也可以用一个全局的异步锁来控制同一时间只有一个对话框创建流程在执行。我们可以把锁放在App级别,这样所有窗口都能共享,不用每个窗口单独维护状态。
首先在App.xaml.cs里定义一个静态的信号量:
public static readonly SemaphoreSlim CloseDialogSemaphore = new SemaphoreSlim(1, 1);
然后修改关闭按钮的事件处理代码:
Windows.UI.Core.Preview.SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += async (sender, args) => { args.Handled = true; // 尝试获取锁,0秒超时,拿不到说明已有对话框在显示,直接返回 if (!App.CloseDialogSemaphore.Wait(0)) { return; } try { ContentDialog locationPromptDialog = new ContentDialog { Title = "Do you want to exit?", Content = "", CloseButtonText = "No", PrimaryButtonText = "Yes" }; ContentDialogResult result = await locationPromptDialog.ShowAsync(); if (result == ContentDialogResult.Primary) { App.Current.Exit(); } } finally { // 不管对话框怎么关闭,最后都要释放锁 App.CloseDialogSemaphore.Release(); } };
这个方案的好处是用户仍然能看到关闭按钮,但重复点击不会触发新的对话框,也不会报错。
你之前碰到的报错信息如下:
System.Runtime.InteropServices.COMException
Message=An async operation was not properly started. Only a single ContentDialog can be open at any time.
Source=Windows
StackTrace:
at Windows.UI.Xaml.Controls.ContentDialog.ShowAsync()
at CORsvr.MainPage.
本质就是因为多次触发ShowAsync(),而UWP不允许同时显示多个ContentDialog,上面两个方案都能完美解决这个问题。
内容的提问来源于stack exchange,提问作者neo

