Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I ask for user input from a background thread?

Tags:

c#

winforms

I'm using an external library load a large and complex file. The call(s) to this library are fairly complex, so I've wrapped them in a couple of static helper methods which nicely take care of caching etc. for me. These methods are then run in the background using Tasks.

During the load process, the library will in some cases throw an Exception stating that a block of the file is malformed and therefore cannot be parsed. These Exceptions are considered "safe", and, if they're swallowed, the library will skip the bad block and happily continue to parse the rest of the file.

When this occurs, I need to show the user a dialog box asking whether or not the file import should be aborted. This works fine as follows:

public static class MyBigFileLoadMethods {
    // private fields for locking, caching, etc.

    public static Load(string filePath, bool cache = true) {
        // validation etc.

        try {
            var data = LoadMethodInDll(filePath);  
        } catch (BadBlockException) {
            if (MessageBox.Show("boom.  continue anyway?") == DialogResult.Yes) {
                // call appropriate dll methods to ignore exception and continue loading
            } else {
                throw;
            }
        }
    }
}

Calling MessageBox.Show() from a method that was designed to be run way in the background feels very wrong, but I haven't come up with a better way that didn't involve so much marshaling and invoking that the code became very difficult to read. Is there a cleaner way to do this or a better way to me to design my loading process?

like image 341
Brian Kintz Avatar asked Oct 29 '25 02:10

Brian Kintz


1 Answers

The appropriate way for a library to do this is via some kind of callback. The simplest implementation would be a delegate returning a bool indicating whether processing should continue. A richer but complicated way would be a policy interface with various methods to implement indicating whether to continue, abort, retry, etc.

Then your UI code provides the callback that shows a message to the user in an appropriate way. Your code to load the library will look like this:

public static class MyBigFileLoadMethods {
    // private fields for locking, caching, etc.

    public static void Load(string filePath, Func<Exception, bool> continueOnException = null, bool cache = true) {
        // validation etc.

        try {
            var data = LoadMethodInDll(filePath);  
        } catch (BadBlockException e) {
            if (continueOnException != null && continueOnException(e))  {
                // call appropriate dll methods to ignore exception and continue loading
            } else {
                throw;
            }
        }
    }
}

Then in your UI code you will want to marshal back to the UI thread. It will look something like this:

MyBigFileLoadMethods.Load("C:\path\to\data", ShowError);

private bool ShowError(Exception e)
{
    if (this.InvokeRequired)
    {
        return (bool)this.Invoke(new Func<Exception, bool>(ShowError), e);
    }

    return MessageBox.Show(string.Format("boom: {0}. continue anyway?", e.Message)) == DialogResult.Yes;
}
like image 147
Mike Zboray Avatar answered Oct 30 '25 16:10

Mike Zboray



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!