C# 关闭所有子WPF窗口并终止等待代码
我正在尝试实现一个系统,用于关闭WPF应用程序中的所有模式和非模式窗口(主应用程序窗口除外)。当这些窗口关闭时,应放弃等待对话框结果的任何代码 到目前为止,我考虑/尝试了两种策略:C# 关闭所有子WPF窗口并终止等待代码,c#,wpf,mvvm,modal-dialog,async-await,C#,Wpf,Mvvm,Modal Dialog,Async Await,我正在尝试实现一个系统,用于关闭WPF应用程序中的所有模式和非模式窗口(主应用程序窗口除外)。当这些窗口关闭时,应放弃等待对话框结果的任何代码 到目前为止,我考虑/尝试了两种策略: 关闭并重新启动应用程序 关闭所有窗口并依靠任务取消异常放弃所有等待对话框结果的代码。(它冒泡到应用程序级别,然后被处理。) 第一个解决方案肯定会让应用程序关闭,并足以自动注销,但我对在等待的对话框关闭后继续执行的代码感到非常不舒服。有没有一个好方法来停止代码的执行 第二种解决方案工作得相对较好(调用代码被中止),但有
ShowDialog
调用时锁定。(至少,当您暂停执行时,它就结束了。)这很奇怪,因为断点清楚地表明,Closed
事件正在我打算关闭的所有窗口上引发。最终用户看到的结果是一个登录屏幕,该屏幕无法单击,但可以通过选项卡进入。真奇怪!尝试以不同优先级调度呼叫失败,但任务延迟100毫秒可能就成功了。(但这不是一个真正的解决方案。)
如果每个打开的弹出窗口都在后台等待一个TaskCompletionSource
,并且在完成TCS后,尝试使用调度程序在其自身上调用Close
,为什么即使在看到引发Closed
事件后,仍有一个(或多个)对话框在ShowDialog
上阻塞?有没有办法正确地将这些调用分派到Close
以便它们成功完成?我是否需要特别说明车窗关闭的顺序
一些伪代码-C#-混合示例:
class PopupService
{
async Task<bool> ShowModalAsync(...)
{
create TaskCompletionSource, publish event with TCS in payload
await and return the TCS result
}
void ShowModal(...)
{
// method exists for historical purposes. code calling this should
// probably be made async-aware rather than relying on the blocking
// behavior of Window.ShowDialog
create TaskCompletionSource, publish event with TCS in payload
rethrow exceptions that are set on the Task after completion but do not await
}
void CloseAllWindows(...)
{
for every known TaskCompletionSource driving a popup interaction
tcs.TrySetCanceled()
}
}
class MainWindow : Window
{
void ShowModalEventHandler(...)
{
create a new PopupWindow and set the owner, content, etc.
var window = new PopupWindow(...) { ... };
...
window.ShowDialog();
}
}
class PopupWindow : Window
{
void LoadedEventHandler(...)
{
...
Task.Run(async () =>
{
try
await the task completion source
finally
Dispatcher.Invoke(Close, DispatcherPriority.Send);
});
register closing event handlers
...
}
void ClosedEventHandler(...)
{
if(we should do something with the TCS)
try set the TCS result so the popup service caller can continue
}
}
class-PopupService
{
异步任务ShowModalAsync(…)
{
创建TaskCompletionSource,使用负载中的TCS发布事件
等待并返回TCS结果
}
void showmodel(…)
{
//方法是出于历史目的而存在的。调用此方法的代码应
//可能是异步感知的,而不是依赖于阻塞
//Window.ShowDialog的行为
创建TaskCompletionSource,使用负载中的TCS发布事件
重新显示任务完成后设置但不等待的异常
}
无效关闭所有窗口(…)
{
对于驱动弹出式交互的每个已知TaskCompletionSource
tcs.TrySetCanceled()
}
}
类主窗口:窗口
{
void ShowModalEventHandler(…)
{
创建新的PopupWindow并设置所有者、内容等。
var窗口=新的PopupWindow(…){…};
...
ShowDialog();
}
}
类弹出窗口:窗口
{
无效加载的排气阀(…)
{
...
Task.Run(异步()=>
{
尝试
等待任务完成源
最后
调用(关闭,DispatcherPriority.Send);
});
注册关闭事件处理程序
...
}
void ClosedEventHandler(…)
{
如果(我们应该对TCS采取措施)
尝试设置TCS结果,以便弹出服务调用方可以继续
}
}
我不确定这是否能解决您的问题,但就我而言,我创建了扩展方法来帮助混合异步代码和窗口生命周期管理。例如,您可以创建一个ShowDialogAsync(),它返回将在窗口实际关闭时完成的任务。如果您请求取消,还可以提供CancellationToken来自动关闭对话框
公共静态类窗口扩展
{
公共静态任务ShowDialogAsync(此窗口,CancellationToken CancellationToken=new CancellationToken())
{
var completionSource=new TaskCompletionSource();
window.Dispatcher.BeginInvoke(新操作(()=>
{
var result=window.ShowDialog();
//对话框关闭时,设置结果以完成返回的任务。如果任务已取消,则将放弃该任务。
completionSource.TrySetResult(结果);
}));
如果(cancellationToken.canbecancelled)
{
//请求取消时收到通知,以便我们可以关闭窗口并取消返回的任务
cancellationToken.Register(()=>window.Dispatcher.BeginInvoke(新操作(()=>
{
completionSource.TrySetCanceled();
window.Close();
})));
}
返回completionSource.Task;
}
}
在UI代码中,可以使用如下所示的ShowDialogAsync()方法。如您所见,当任务被取消时,对话框将关闭,并引发OperationCanceledException异常以停止代码流
private async void按钮\u单击(对象发送方,路由目标)
{
尝试
{
YourDialog=新建YourDialog();
CancellationTokenSource=新的CancellationTokenSource(TimeSpan.FromSeconds(3));
wait dialog.ShowDialogAsync(source.Token);
}
捕捉(操作取消例外)
{
MessageBox.Show(“操作已取消”);
}
}
使用窗口。ShowDialog
可创建嵌套的Dispather
消息循环。使用await
,可以在该内部循环上“跳转”并继续执行async
方法,例如:
var dialogTask=window.ShowDialogAsync()
foreach(Window item in App.Current.Windows)
{
if(item!=this)
item.Close();
}