0

I've made my own custom Task Dialog window in WPF. I wanted it to follow the style of my application, and I have some custom content to place on it, so I prefer not using the win32 task dialog.

My application uses asynchronous tasks (async/await) to load data from the database. If there's an error/exception while loading data, the ui thread will show a (custom) TaskDialog with error information.

The win32 task dialog will show one instance of itself, and other pending TaskDialogs to show will only show up after the first has returned (or actually it seems to block awaited task continuations) The problem is that my custom task dialog is showing multiple errors at the same time (from awaited tasks), showing up multiple times on the screen.

The way I understand the issue (and perhaps I'm wrong here) is that despite using ShowDialog(), my task dialog is running on the UI thread and pumping messages. Further synchronous main window code is not executing, but if a task finishes awaiting, it returns to the UI thread to execute, and assuming there was a data load error, another (custom) task dialog is shown:

1) Main window starts data loading in multiple awaited tasks
2) awaited task returns with error/exception
3) Custom task dialog window is shown with ShowDialog()
4) While custom task dialog still showing, another awaited task returns with an error as well
5) Continuation code executes on UI thread which is currently showing task dialog
6) A second custom task dialog is shown over the first
etc

I really can't figure out how the Win32 taskdialog is blocking multiple calls to itself while keeping the UI thread alive. I've tried many different solutions, and nothing seems to work properly.

I realize I could make my task dialog async, use a semaphore and WaitAsync on it, but since my task dialog needs to return a TaskDialogResult, I'd need to await every call to my task dialog which would be way too many code changes.

I know I must be missing something here, anyone have any ideas on this?

What I have tried:

Making my task dialog call async, using a semaphore and WaitAsync on it works, but my task dialog needs to return a TaskDialog result, and so I'd have to await every call to it, unfortunately that's too many code changes.

I've tried running my task dialog in a separate thread and blocking the main UI thread until the task dialog returns. That does give me the desired effect, but blocking the main UI thread is causing issues elsewhere. This also doesn't seem like the proper way to do it.

Richard R
  • 1
  • 2
  • Use locks and lock your custom messagebox function. – Sv Sv Nov 21 '17 at 18:13
  • @SvSv I've tried that, but a lock won't actually lock on the same thread (the UI thread in this case) – Richard R Nov 21 '17 at 18:15
  • what about having your errors go into a queue, and your ShowDialog in a loop till the queue is empty? – Joel Lucsy Nov 21 '17 at 19:40
  • @JoelLucsy That would be a good solution except I need my TaskDialog to potentially return a result (clicked button), and the caller should wait until getting that result. If not it's a lot of code to redo. I'm basically just trying to replace win32 TaskDialog with the same functionality but somehow win32 TaskDialog blocks awaited task continuations while still processing other messages – Richard R Nov 21 '17 at 19:54
  • use a semaphore – Steve Nov 21 '17 at 20:08
  • The TaskDialog, being Win32, may run its own message-loop while its being displayed and not process any WPF specific messages. When WPF regains control, it starts processing its own queue. WPF maintains a queue of things to run on the GUI thread and triggers the draining of this queue by sending specific messages. Maybe what you should do is to start a task that waits on a semaphore, then, BeginInvoke back to the GUI thread to display your dialog and process the result. – Joel Lucsy Nov 21 '17 at 20:11
  • I'm going to agree somewhat with @Joel, but I'll take it a step further. Instead of looping through TaskDialogs which will be annoying if you have a lot of errors, why not do something more sophisticated and either aggregate the errors or enhance the TaskDialog to allow the use to scroll through multiple errors. Maybe < > arrow buttons or something like that? – SledgeHammer Nov 21 '17 at 20:22
  • @JoelLucsy Thanks for your replies by the way. The TaskDialog seperate message loop is exactly what I was thinking. Unfortunately with starting a task that waits on a semaphore, I still can't imagine how I'd return a result and force the caller to wait on that result, without redoing a bunch of existing code that just goes something like result = TaskDialog.Show(bla) I have figured out a hacky way of doing it, by having a flag when a dialog is shown and any other dlg calls spin in a loop and keep the dispatcher pumping messages while waiting on that flag. Probably not the best solution. – Richard R Nov 21 '17 at 20:27

0 Answers0