Replacing a Custom DoEvents-Function

48 views Asked by At

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.

1

There are 1 answers

0
Yair Maron On

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