public static void DoWorkWithProgressDialog(BloomWebSocketServer socketServer, string socketContext, Func <Form> makeDialog, Func <IWebSocketProgress, bool> doWhat, Action <Form> doWhenMainActionFalse = null) { var progress = new WebSocketProgress(socketServer, socketContext); // NOTE: This (specifically ShowDialog) blocks the main thread until the dialog is closed. // Be careful to avoid deadlocks. using (var dlg = makeDialog()) { // For now let's not try to handle letting the user abort. dlg.ControlBox = false; var worker = new BackgroundWorker(); worker.DoWork += (sender, args) => { // A way of waiting until the dialog is ready to receive progress messages while (!socketServer.IsSocketOpen(socketContext)) { Thread.Sleep(50); } bool waitForUserToCloseDialogOrReportProblems; try { waitForUserToCloseDialogOrReportProblems = doWhat(progress); } catch (Exception ex) { // depending on the nature of the problem, we might want to do more or less than this. // But at least this lets the dialog reach one of the states where it can be closed, // and gives the user some idea things are not right. socketServer.SendEvent(socketContext, "finished"); waitForUserToCloseDialogOrReportProblems = true; progress.MessageWithoutLocalizing("Something went wrong: " + ex.Message, ProgressKind.Error); } // stop the spinner socketServer.SendEvent(socketContext, "finished"); if (waitForUserToCloseDialogOrReportProblems) { // Now the user is allowed to close the dialog or report problems. // (ProgressDialog in JS-land is watching for this message, which causes it to turn // on the buttons that allow the dialog to be manually closed (or a problem to be reported). socketServer.SendBundle(socketContext, "show-buttons", new DynamicJson()); } else { // Just close the dialog dlg.Invoke((Action)(() => { if (doWhenMainActionFalse != null) { doWhenMainActionFalse(dlg); } else { dlg.Close(); } })); } }; worker.RunWorkerAsync(); dlg.ShowDialog(); // returns when dialog closed } }