0

I have a topmost WinForm with a simple button that executes some commands asynchronously:

        private async void button1_Click(object sender, EventArgs e)
    {
        await System.Threading.Tasks.Task.Run(() =>
        {
            System.Threading.Thread.Sleep(2000);

            //Problem1: not works as "modal" dialog box (Main form remains active!) 
            new Form().ShowDialog();

            //Problem2: not shown as "modal" messagebox (Main form remains active!)
            MessageBox.Show("Test");
        });
    }

Inside the async function are the Messagebox.Show() and ShowDialog() methods, BUT:

Problem 1(solved): The new form does not open as modal dialog box (the main form is still active and accessible!)

Problem 2(solved): The MessageBox.Show() method doesn't behave as modal dialog box (the main form is still active and accessible!).

I need async-await to prevent the main UI from freezing, but I also want messageboxes (and sub-forms) inside to be displayed as modal dialog box. How can i show modal dialog boxes (on the main topmost Form) via async method?

Thanks

  • The problem is not with `async`, but with `Task.Run()`, which starts a _new_ thread. You should not access the UI thread from a background thread anyway. Why exactly are you calling `Task.Run()`? If you show your real code, we can give you suggestions. The current code in the question doesn't really need `Task.Run()`. If all you need is a delay, just call `await Task.Delay(2000);` and then show your form and/or Messagebox. – 41686d6564 stands w. Palestine Jun 09 '21 at 17:37
  • @41686d6564 It was just a simple simulation of my problem. Due to time consuming tasks, I have to use async and TaskRun methods – user11448245 Jun 09 '21 at 17:44
  • If those tasks are CPU-bound, you're right about using `Task.Run()` but you might consider showing the form/MessageBox after it's finished. If that's not an option, we need to see the actual code to suggest a better solution. On the other hand, if those tasks are IO-bound, then you probably shouldn't be using `Task.Run()` in the first place. Ref: [What do the terms “CPU bound” and “I/O bound” mean?](https://stackoverflow.com/q/868568/8967612). – 41686d6564 stands w. Palestine Jun 09 '21 at 17:51
  • @41686d6564 Thank you. I am not familiar with these two concepts and should read more, but in general: I have a heavy function (that contains messageboxes and sub-forms) that I do not want to freeze the main topmost UI but I also want the messages (or sub-forms) to be displayed on the main form as modal dialog boxes. – user11448245 Jun 09 '21 at 18:02
  • Have you checked out this? [Async ShowDialog](https://stackoverflow.com/questions/33406939/async-showdialog) – Theodor Zoulias Jun 09 '21 at 18:43
  • @TheodorZoulias Yes, but I couldn't find anything to solve my problem – user11448245 Jun 09 '21 at 19:15
  • noseratio has posted a neat [`ShowDialogAsync`](https://stackoverflow.com/a/33411037/11178549) extension method there, that can be used to open a popup as a modal window, without freezing the parent form. I have tested it some time ago and it worked. Just make sure that you don't add background threads in the mix with `Task.Run`. Or if you do, that you avoid any interaction with the UI from the background thread. – Theodor Zoulias Jun 09 '21 at 21:58
  • If you want to modify the UI while the heavy function is running on the background thread, including showing popup messages, the modern approach is to use one or more `Progress` objects. You can see an example [here](https://stackoverflow.com/questions/12414601/async-await-vs-backgroundworker/64620920#64620920). The old approach is the [`Invoke` and `BeginInvoke`](https://stackoverflow.com/questions/11995466/c-sharp-calling-form-show-from-another-thread) methods. – Theodor Zoulias Jun 09 '21 at 22:11
  • @TheodorZoulias ShowDialogAsync seems working! Thanks so much. In the meantime, I have found a solution to the second problem (MessageBox.Show). see updated parts in original post – user11448245 Jun 10 '21 at 08:51
  • I would suggest to post the solutions as an answer. Including an answer as part of the question is frowned upon here. On the other hand answering your own question [is allowed and encouraged](https://stackoverflow.com/help/self-answer). – Theodor Zoulias Jun 10 '21 at 08:59
  • @TheodorZoulias Thank you for your suggestion. As you said, I posted the answers separately – user11448245 Jun 10 '21 at 09:06

2 Answers2

1

Solution for problem-1: ShowDialogAsync extension method solves the problem.

Solution for problem-2:

        private async void button1_Click(object sender, EventArgs e)
    {

        var handle = this.Handle;
        await System.Threading.Tasks.Task.Run(() =>
        {
            System.Threading.Thread.Sleep(2000);

            //Solution for Problem2:

            NativeWindow win32Parent = new NativeWindow();
            win32Parent.AssignHandle(handle);
            //Works as expected (Topmost and Modal):
            MessageBox.Show(win32Parent, "Test");
        });
    }

Related topic

-1

Task.Run() runs work in background thread. In common you shouldn't show windows from background threads, but you can. To solve your problem you need to use UI thread Dispatcher.

Application.Current.Dispatcher.Invoke(() => MessageBox.Show("Test"));

Or

Application.Current.Dispatcher.Invoke(() => new Form().ShowDialog());