在后台操作中间显示一个模态 UI 并继续

编程入门 行业动态 更新时间:2024-10-28 08:20:50
本文介绍了在后台操作中间显示一个模态 UI 并继续的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我有一个 WPF 应用程序运行一个使用 async/await 的后台任务.任务是随着应用程序的进展更新应用程序的状态 UI.在这个过程中,如果满足了某个条件,我需要展示一个模态窗口让用户知道这样的事件,然后继续处理,现在也更新那个模态窗口的状态UI.

I have a WPF application running a background task which uses async/await. The task is updating the app's status UI as it progresses. During the process, if a certain condition has been met, I am required to show a modal window to make the user aware of such event, and then continue processing, now also updating the status UI of that modal window.

这是我试图实现的草图版本:

This is a sketch version of what I am trying to achieve:

async Task AsyncWork(int n, CancellationToken token) { // prepare the modal UI window var modalUI = new Window(); modalUI.Width = 300; modalUI.Height = 200; modalUI.Content = new TextBox(); using (var client = new HttpClient()) { // main loop for (var i = 0; i < n; i++) { token.ThrowIfCancellationRequested(); // do the next step of async process var data = await client.GetStringAsync("www.bing/search?q=item" + i); // update the main window status var info = "#" + i + ", size: " + data.Length + Environment.NewLine; ((TextBox)this.Content).AppendText(info); // show the modal UI if the data size is more than 42000 bytes (for example) if (data.Length < 42000) { if (!modalUI.IsVisible) { // show the modal UI window modalUI.ShowDialog(); // I want to continue while the modal UI is still visible } } // update modal window status, if visible if (modalUI.IsVisible) ((TextBox)modalUI.Content).AppendText(info); } } }

modalUI.ShowDialog() 的问题在于它是一个阻塞调用,因此处理停止,直到对话框关闭.如果窗口是非模态的,那不会有问题,但它必须是模态的,如项目要求所规定的那样.

The problem with modalUI.ShowDialog() is that it is a blocking call, so the processing stops until the dialog is closed. It would not be a problem if the window was modeless, but it has to be modal, as dictated by the project requirements.

有没有办法用 async/await 解决这个问题?

Is there a way to get around this with async/await?

推荐答案

这可以通过异步执行 modalUI.ShowDialog() 来实现(在 UI 线程的消息循环的未来迭代中).ShowDialogAsync 的以下实现通过使用 TaskCompletionSource (EAP 任务模式)和SynchronizationContext.Post.

This can be achieved by executing modalUI.ShowDialog() asynchronously (upon a future iteration of the UI thread's message loop). The following implementation of ShowDialogAsync does that by using TaskCompletionSource (EAP task pattern) and SynchronizationContext.Post.

这样的执行工作流可能有点难以理解,因为您的异步任务现在分布在两个独立的 WPF 消息循环中:主线程的一个和新的嵌套的一个(由 ShowDialog 开始).IMO,这完全没问题,我们只是利用了 C# 编译器提供的 async/await 状态机.

Such execution workflow might be a bit tricky to understand, because your asynchronous task is now spread across two separate WPF message loops: the main thread's one and the new nested one (started by ShowDialog). IMO, that's perfectly fine, we're just taking advantage of the async/await state machine provided by C# compiler.

虽然,当您的任务结束而模态窗口仍处于打开状态时,您可能希望等待用户关闭它.这就是 CloseDialogAsync 在下面所做的.此外,您可能应该考虑用户在任务中间关闭对话框的情况(AFAIK,WPF 窗口不能重复用于多个 ShowDialog 调用).

Although, when your task comes to the end while the modal window is still open, you probably want to wait for user to close it. That's what CloseDialogAsync does below. Also, you probably should account for the case when user closes the dialog in the middle of the task (AFAIK, a WPF window can't be reused for multiple ShowDialog calls).

以下代码对我有用:

using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace WpfAsyncApp { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Content = new TextBox(); this.Loaded += MainWindow_Loaded; } // AsyncWork async Task AsyncWork(int n, CancellationToken token) { // prepare the modal UI window var modalUI = new Window(); modalUI.Width = 300; modalUI.Height = 200; modalUI.Content = new TextBox(); try { using (var client = new HttpClient()) { // main loop for (var i = 0; i < n; i++) { token.ThrowIfCancellationRequested(); // do the next step of async process var data = await client.GetStringAsync("www.bing/search?q=item" + i); // update the main window status var info = "#" + i + ", size: " + data.Length + Environment.NewLine; ((TextBox)this.Content).AppendText(info); // show the modal UI if the data size is more than 42000 bytes (for example) if (data.Length < 42000) { if (!modalUI.IsVisible) { // show the modal UI window asynchronously await ShowDialogAsync(modalUI, token); // continue while the modal UI is still visible } } // update modal window status, if visible if (modalUI.IsVisible) ((TextBox)modalUI.Content).AppendText(info); } } // wait for the user to close the dialog (if open) if (modalUI.IsVisible) await CloseDialogAsync(modalUI, token); } finally { // always close the window modalUI.Close(); } } // show a modal dialog asynchronously static async Task ShowDialogAsync(Window window, CancellationToken token) { var tcs = new TaskCompletionSource<bool>(); using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true)) { RoutedEventHandler loadedHandler = (s, e) => tcs.TrySetResult(true); window.Loaded += loadedHandler; try { // show the dialog asynchronously // (presumably on the next iteration of the message loop) SynchronizationContext.Current.Post((_) => window.ShowDialog(), null); await tcs.Task; Debug.Print("after await tcs.Task"); } finally { window.Loaded -= loadedHandler; } } } // async wait for a dialog to get closed static async Task CloseDialogAsync(Window window, CancellationToken token) { var tcs = new TaskCompletionSource<bool>(); using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true)) { EventHandler closedHandler = (s, e) => tcs.TrySetResult(true); window.Closed += closedHandler; try { await tcs.Task; } finally { window.Closed -= closedHandler; } } } // main window load event handler async void MainWindow_Loaded(object sender, RoutedEventArgs e) { var cts = new CancellationTokenSource(30000); try { // test AsyncWork await AsyncWork(10, cts.Token); MessageBox.Show("Success!"); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } } }

下面是一种稍微不同的方法,它使用 Task.Factory.StartNew 异步调用 modalUI.ShowDialog().返回的 Task 可以稍后等待,以确保用户已关闭模态对话框.

Below is a slightly different approach which uses Task.Factory.StartNew to invoke modalUI.ShowDialog() asynchronously. The returned Task can be awaited later to make sure the user has closed the modal dialog.

async Task AsyncWork(int n, CancellationToken token) { // prepare the modal UI window var modalUI = new Window(); modalUI.Width = 300; modalUI.Height = 200; modalUI.Content = new TextBox(); Task modalUITask = null; try { using (var client = new HttpClient()) { // main loop for (var i = 0; i < n; i++) { token.ThrowIfCancellationRequested(); // do the next step of async process var data = await client.GetStringAsync("www.bing/search?q=item" + i); // update the main window status var info = "#" + i + ", size: " + data.Length + Environment.NewLine; ((TextBox)this.Content).AppendText(info); // show the modal UI if the data size is more than 42000 bytes (for example) if (data.Length < 42000) { if (modalUITask == null) { // invoke modalUI.ShowDialog() asynchronously modalUITask = Task.Factory.StartNew( () => modalUI.ShowDialog(), token, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); // continue after modalUI.Loaded event var modalUIReadyTcs = new TaskCompletionSource<bool>(); using (token.Register(() => modalUIReadyTcs.TrySetCanceled(), useSynchronizationContext: true)) { modalUI.Loaded += (s, e) => modalUIReadyTcs.TrySetResult(true); await modalUIReadyTcs.Task; } } } // update modal window status, if visible if (modalUI.IsVisible) ((TextBox)modalUI.Content).AppendText(info); } } // wait for the user to close the dialog (if open) if (modalUITask != null) await modalUITask; } finally { // always close the window modalUI.Close(); } }

更多推荐

在后台操作中间显示一个模态 UI 并继续

本文发布于:2023-06-09 06:28:11,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/596844.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:后台   操作   模态   UI

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!