0

I've been recently introduced to a project that uses WPF to visualize some work. The application uses some custom windows to display a popup window. There is a bug in the application where this custom popup window will not go away when clicking "OK", or rather it seems to re-appear immediately.

From what I can tell, the issue might be due to a custom DoEvents function based on the infamous DoEvents in VB6. I'm having trouble implementing a better solution for this, because this DoEvents locks the user into the custom popup window, but does not block the main thread and my attempt at an async solution failed.

This is the DoEvents implementation:

[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
 DispatcherFrame frame = new DispatcherFrame();
 Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
 Dispatcher.PushFrame(frame);
 Thread.Sleep(10);
}
public static object ExitFrame(object f)
{
 ((DispatcherFrame)f).Continue = false;
 return null;
}

And this is the function that uses it (simplified):

public static DialogResult Show(string view, string caption)
{
 if (dialogIsOpen)
 return DialogResult.Cancel;
 dialogIsOpen = true;
 // Instantiates the view and sets it in the region.
 MyApplication.OpenView(view, caption);
 object? result = default;
 while ((result = MyApplication.GetValue("MyResult") == null)
 MyApplication.DoEvents();
 // Clears the region.
 MyApplication.ResetView();
 dialogIsOpen = false;
 return result is DialogResult ? (DialogResult)result : DialogResult.Cancel;
}

The instantiated popup view will write the value "MyResult", when the user confirms the popup.

I tried implementing the same logic using Task, but it ended up freezing my application (and not showing the popup at all).

public static DialogResult Show(string view, string caption)
{
 if (dialogIsOpen)
 return DialogResult.Cancel;
 dialogIsOpen = true;
 // Instantiates the view and sets it in the region.
 MyApplication.OpenView(view, caption);
 var task = Task.Run(async () =>
 {
 object? result = default;
 while ((result = MyApplication.GetValue("MyResult") == null)
 await Task.Delay(10);
 return result;
 });
 object? result = task.Result;
 // Clears the region.
 MyApplication.ResetView();
 dialogIsOpen = false;
 return result is DialogResult ? (DialogResult)result : DialogResult.Cancel;
}

I think the problem is that the main thread waits for the async code to finish (and blocks user input). I lack some multitasking experience so I don't know how to fix this.

asked Jun 6, 2023 at 6:01
11
  • Do you really need the window to be custom? Could it just be replaced by something "oridinary"? Commented Jun 6, 2023 at 6:11
  • 1
    ^^ That being said: I believe you are waisting your time trying to fix this. Scrap it and do it right. learn.microsoft.com/en-us/archive/msdn-magazine/2019/may/… Commented Jun 6, 2023 at 6:18
  • 1
    ^^ No. Don't do that. If you go async, go all the way. And that doesn't seem to be an option, here. The one solution, I could imagine would work, would be to use a TaskCompletionSource but that would still mean you needed to make Show an async Task. Commented Jun 6, 2023 at 6:25
  • 3
    But in fact, a Dialog (aka Modal in other contexts) is supposed to be blocking, because you need the result before you can continue. By not implementing this the standard WPF-way, your predecessors digged your grave. I am 100% confident, your time is much better spent in fixing the underlying issue ( not having created a proper WPF Dialog ) than patching the fuse of that timebomb. Commented Jun 6, 2023 at 6:41
  • 1
    Something like that, yes. Maybe read some tutorial on WPF custom dialogs, so you get the pitfalls and stuff. It should be extensively documented. Just be careful what is relevant to your "kind" of WPF (.NET (Core+) vs .Net Framework) Commented Jun 6, 2023 at 6:49

1 Answer 1

2

The usage of DoEvents (maybe in the first place) was done to avoid using proper multi-threading.

The right solution would be using callbacks, or async functions, or to subscribe to events.

for example, instead of creating a loop which is waiting for some value to be filled- you can create an event and raise it when fulfilled.

this way your code won't be "waiting" and holding the thread blocked

answered Jun 6, 2023 at 6:27
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.