Modal Workflow
By "Modal Workflow" we mean 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.

Modal Workflow

The code and images below illustrate how Wisej handles modal execution of code:
C#
VB.NET
1
void button1_Click(object sender, EventArgs e)
2
{
3
if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
4
{
5
this.button1.BackColor = Color.Green;
6
this.button1.Text = "You selected: Yes!";
7
}
8
}
Copied!
1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
2
If MessageBox.Show("Are you sure?", buttons:=MessageBoxButtons.YesNo) = DialogResult.Yes Then
3
Me.Button1.BackColor = Color.Green
4
Me.Button1.Text = "You selected: Yes!"
5
End If
6
End Sub
Copied!
Before clicking the button.
After clicking the button.
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.
After the user selected Yes.
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 Dialogs

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:
C#
VB.NET
1
void button1_Click(object sender, EventArgs e)
2
{
3
using (new dialog = new EnterCustomerAddress())
4
{
5
if (dialog.ShowDialog(this) == DialogResult.OK)
6
{
7
AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text);
8
}
9
}
10
}
Copied!
1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
2
Using dialog As New EnterCustomerAddress()
3
If dialog.ShowDialog(Me) = DialogResult.OK Then
4
AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text)
5
End If
6
End Using
7
End Sub
Copied!
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(...).
C#
VB.NET
1
void async button1_Click(object sender, EventArgs e)
2
{
3
using (new dialog = new EnterCustomerAddress())
4
{
5
if (await dialog.ShowDialogAsync(this) == DialogResult.OK)
6
{
7
AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text);
8
}
9
}
10
}
Copied!
1
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
2
Using dialog As New EnterCustomerAddress()
3
If Await dialog.ShowDialogAsync(Me) = DialogResult.OK Then
4
AlertBox.Show(dialog.textBoxAddress.Text + " " + dialog.textBoxState.Text)
5
End If
6
End Using
7
End Sub
Copied!
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.
C#
VB.NET
1
// Open the combo box drop down list and wait
2
// for the user to select an item or close the combo box.
3
private void button1_Click(object sender, EventArgs e)
4
{
5
this.comboBox1.DroppedDown = true;
6
this.comboBox1.DropDownClosed += ComboBox1_DropDownClosed;
7
this.comboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;
8
9
// wait for the user to either pick an item or close the drop down.
10
var result = Application.DoModal(this.comboBox1);
11
12
// code execution will resume here.
13
}
14
15
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
16
{
17
this.comboBox1.DropDownClosed -= ComboBox1_DropDownClosed;
18
Application.EndModal(this.comboBox1, DialogResult.OK);
19
}
20
21
private void ComboBox1_DropDownClosed(object sender, EventArgs e)
22
{
23
Application.EndModal(this.comboBox1, DialogResult.Cancel);
24
}
Copied!
1
' Open the combo box drop down list And wait
2
' for the user to select an item Or close the combo box.
3
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
4
5
Me.ComboBox1.DroppedDown = True
6
AddHandler Me.ComboBox1.DropDownClosed, AddressOf ComboBox1_DropDownClosed
7
AddHandler Me.ComboBox1.SelectedIndexChanged, AddressOf ComboBox1_SelectedIndexChanged
8
9
' wait for the user to either pick an item Or close the drop down.
10
Dim result As DialogResult = Application.DoModal(Me.ComboBox1)
11
12
' code execution will resume here.
13
14
End Sub
15
16
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs)
17
18
RemoveHandler Me.ComboBox1.DropDownClosed, AddressOf ComboBox1_DropDownClosed
19
Application.EndModal(Me.ComboBox1, DialogResult.OK)
20
21
End Sub
22
23
Private Sub ComboBox1_DropDownClosed(sender As Object, e As EventArgs)
24
25
Application.EndModal(Me.ComboBox1, DialogResult.Cancel)
26
27
End Sub
Copied!
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.