Modal Workflow

In a program, a modal workflow is a mode of operation in which a particular task or feature must be completed before the user can proceed to other tasks or features. This is typically implemented by displaying a modal window or dialog box that requires the user to interact with it before they can continue using the software.

Modal workflows are often used in situations where it is important to ensure that the user has completed a particular task or provided certain information before moving on. For example, a modal workflow might be used to prompt the user to save their work before closing a document, or to enter their login credentials before accessing a restricted area of the software.

Modal workflows can be useful for guiding the user through a specific set of steps or for drawing their attention to important information or actions. However, if overused or implemented poorly, modal workflows can also be frustrating for users and disrupt the flow of their work.

In a web application, supporting a modal workflow means the ability to suspend the execution of server-side code, waiting for user input, and to be able to resume the execution flow using any information that may have been received from the user.

It's simple when running a desktop application. It's very hard when running a web application where the code is being executed on the server and the UI is completely asynchronous and running on different machines.

The code and images below illustrate how Wisej.NET handles modal execution of code:

void button1_Click(object sender, EventArgs e)
{
    if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
    {
          this.button1.BackColor = Color.Green;
          this.button1.Text = "You selected: Yes!";
    }
}

Now the server code is suspended, waiting for the user to choose. After the user clicks Yes or No on the client-side, the server code will resume exactly where it was suspended.

Wisej can handle nested modal states, custom dialog results and can reference controls in the modal dialog, as well as any data entered by the user .

Modal workflow works for message boxes and forms (dialog boxes) with any type of control, data binding, and code-behind. The example above, modified to use a modal dialog, looks like this:

void button1_Click(object sender, EventArgs e)
{
  using (new dialog = new EnterCustomerAddress())
  {
    if (dialog.ShowDialog(this) == DialogResult.OK)
    {
        AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text);
    }
  }
}

Regular modal dialogs or message boxes suspend the current thread while waiting for user input. By default the system can handle about 32,000 threads. While the thread is suspended it doesn't use any CPU time.

Async Modal Dialogs

Wisej fully supports the async/await programming pattern also for modal dialogs.

Using the await keyword a modal dialog can wait to be closed without suspending the thread. Just use the "Async" version of the ShowDialog method.

The async version of the MessageBox is MessageBox.ShowAsync(...).

void async button1_Click(object sender, EventArgs e)
{
  using (new dialog = new EnterCustomerAddress())
  {
    if (await dialog.ShowDialogAsync(this) == DialogResult.OK)
    {
        AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text);
    }
  }
}

Asynchronous modal dialogs or message boxes don't use any thread while waiting for the user input.

Custom Modal State

You don't need to use a modal dialog or message box to "go modal" and wait for user input. Wisej provides the Application.DoModal(component) method to let your code go modal at any time.

For example, the PropertyGrid uses the Application.DoModal() to suspend the code flow while waiting for the user to close the drop down selection box. Use Application.EndModal(component, result) to terminate the last modal state: modal states are stacked.

// Open the combo box drop down list and wait
// for the user to select an item or close the combo box.
private void button1_Click(object sender, EventArgs e)
{
	this.comboBox1.DroppedDown = true;
	this.comboBox1.DropDownClosed += ComboBox1_DropDownClosed;
	this.comboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;

	// wait for the user to either pick an item or close the drop down.
	var result = Application.DoModal(this.comboBox1);

	// code execution will resume here.
}

private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
	this.comboBox1.DropDownClosed -= ComboBox1_DropDownClosed;
	Application.EndModal(this.comboBox1, DialogResult.OK);
}

private void ComboBox1_DropDownClosed(object sender, EventArgs e)
{
	Application.EndModal(this.comboBox1, DialogResult.Cancel);
}

The code snippet above opens a drop down and waits for the user to either select an item or close the combo box. If the user closes it without a selection, DoModal returns DialogResult.Cancel, otherwise it returns DialogResult.OK.

It's quite a powerful way to manage code execution and synchronize it with user actions.

We'll add Application.DoModalAsync(component) in a future build.

Last updated