Introduction

The Hello World project is a simple Windows Forms Application. It sends a text to an imaginary server and gets back the inverted text. As this operation could take long, it will be executed asynchronously. In case an error occures during the server request, an error handling will be implemented. Finally we add a logging component to trace the message flow in the program.

You can download the following hello world application or you can make it step by step. You can also watch it as a video.
Download the hello world source
Watch video

Installing MCM Visual Studio Templates

Download the file Windows DLL-Libraries and Visual Studio 2010 Templates and decompress it.

Copy ItemTemplates to:
MyDocuments\Visual Studio 2010\Templates\ItemTemplates\Visual C#

Copy ProjectTemplates to:
MyDocuments\Visual Studio 2010\Templates\ProjectTemplates\Visual C#

Creating the Project

Start Visual Studio 2010 and Select File->New->Project and search for the Template "MCM Windows Forms Application".

MCM Framework Demo - Add Windows Forms Application.png

New project will be created

MCM Framework Demo - Solution Explorer.png

Creating UI

Open the Form1 and create following controls: inputTextBox, executeButton, outputTextBox.

MCM Framework Demo - Main Form.png

Creating classes for asynchronous operation

Click right mouse button on the Components folder in the solution explorer and select "New Item"
In the dialog box select "MCM Async Operation Classes" template and name it "ReverseText".

MCM Framework Demo - Add Asynchronous Operation Classes.png

A bunch of classes will be created:
  • ReverseTextRequestMessage
  • ReverseTextResponseMessage
  • ReverseTextCommand
  • ReverseTextCommandArgument
  • ReverseTextCommandResult
  • ReverseTextComponent

Defining ReverseTextRequestMessage

This message will be broadcasted after the user clicks executeButton. The ReverseTextRequestMessage contains parameters needed for executing ReverseTextCommand. In this case define only the InputText property.

public class ReverseTextRequestMessage : AsyncOperationRequestMessage
{
    public ReverseTextRequestMessage(string inputText)
    {
        InputText = inputText;
    }

    public string InputText { get; set; }
}

Defining ReverseTextResponseMessage

This message will be broadcasted after the asynchronous operation is completed. The only parameter of the response message is the OutputText.

public class ReverseTextResponseMessage : AsyncOperationResponseMessage
{
    public ReverseTextResponseMessage(Exception error)
        : base(error)
    {
    }

    public string OutputText { get; set; }
}
ReverseTextResponseMessage is inherited from AsyncOperationResponseMessage which informs about the last error of the asynchronous operation if any error has occured and if this operation was cancelled.

Defining ReverseTextCommandArgument

ReverseTextCommandArgument contains the whole environment required by ReverseTextCommand. In advanced scenarios it can contain service references or other references to abstract classes or interfaces. In this simple case the only required parameter is the InputText.

public class ReverseTextCommandArgument
{
    public string InputText { get; set; }
}

Defining ReverseTextCommandResult

The command result provides only one single property - OutputText.

public class ReverseTextCommandResult
{
    public string OutputText { get; set; }
}

Defining ReverseTextComponent

After receiving ReverseTextRequestMessage ReverseTextComponent creates the command argument and starts the command asynchronously.

[MessageSubscriber(McmComponentContainer.MessagesChannelName)]
private void handleReverseTextRequestMessage(ReverseTextRequestMessage m)
{
    // create command argument
    ReverseTextCommandArgument arg = new ReverseTextCommandArgument();

    arg.InputText = m.InputText;

    // create the command using the argument above
    ReverseTextCommand c = new ReverseTextCommand(arg);

    // execute the command
    _ReverseTextCommandExecutor.ExecuteAsync(c);
}

After the command is executed the component broadcasts ReverseTextResponseMessage.

private void on_ReverseTextCommandExecutor_ExecuteCompleted(object sender,
                                                            CommandEventArgs<ReverseTextCommand> e)
{
    // create the response message telling about the last error
    ReverseTextResponseMessage m = new ReverseTextResponseMessage(e.Command.Error);
    if (!m.HasError)
    {
        // no error occured, 
        // set message properties from the command result
        m.OutputText = e.Command.Result.OutputText;
    }

    // Broadcast the message
    Components.Messages.Post(m);
}

Defining ReverseTextCommand

In a real world ReverseTextCommand.ExecuteCore() method could connect to a remote service or make a database query. In this simple scenario wie simulate only such a lengthy operation.

protected override void ExecuteCore()
{
    // simulate lengthy operation
    Thread.Sleep(2000);

    char[] a = Argument.InputText.ToCharArray();
    Array.Reverse(a);
    Result = new ReverseTextCommandResult();
    Result.OutputText = new string(a);
}

Preserving the current output

Instead storing the current output in ReverseTextComponent we create additional component responsible only for managing the current output. Click the right mouse button on the Components folder and select "Add Item". In the dialog box select "MCM Component" template and name it CurrentOutputTextComponent.

MCM Framework Demo - Add Component.png

CurrentOutputTextComponent updates CurrentOutputText after receiving ReverseTextResponseMessage and broadcasts CurrentOutputTextChangedMessage after this property changes.

[Export(typeof(McmComponent))]
public class CurrentOutputTextComponent : McmComponent
{
    private string _currentOutputText;
    public string CurrentOutputText
    {
        get { return _currentOutputText; }
        private set
        {
            _currentOutputText = value;
            Components.Messages.Post(new CurrentOutputTextChangedMessage());
        }
    }

    [MessageSubscriber(McmComponentContainer.MessagesChannelName)]
    private void handleReverseTextResponseMessage(ReverseTextResponseMessage m)
    {
        if (m.HasError)
        {
            return;
        }
        CurrentOutputText = m.OutputText;
    }
}

Starting the command

Clicking the executeButton broadcasts ReverseTextRequestMessage.

private void executeButton_Click(object sender, System.EventArgs e)
{
    Env.PostMessage(new ReverseTextRequestMessage(inputTextBox.Text));
}
After receiving the message ReverseTextComponent creates RevertTextCommand and starts it asynchronously.

Updating the UI

After adding Form1 to the component container its methods are examined. All methods marked with MessageSubscriberAttribute are added to the invocation list of a particular message. Additional parameter UIThreadSynchronizationMode.PostAsynchronousInUIThread forces the invocation in the UI thread.

[MessageSubscriber(McmComponentContainer.MessagesChannelName, UIThreadSynchronizationMode.PostAsynchronousInUIThread)]
private void handleCurrentOutputTextChangedMessage(CurrentOutputTextChangedMessage m)
{
    outputTextBox.Text = Env.Components.First<CurrentOutputTextComponent>().CurrentOutputText;
}
Interesting is updating the outputTextBox directly accessing CurrentOutputTextComponent. Alternatively we could transfer the current value of the output text using CurrentOutputTextChangedMessage.

Updating the working state

After clicking executeButton the input text is shown reversed in the output box after ca. 2 sec. However deactivating the executeButton during the executing would be nice. We define two additional methods in Form1 and subscribe them to AsyncOperationRequestMessage and AsyncOperationResponseMessage message types. Broadcasting all messages inherited from the above ones invokes these methods.

[MessageSubscriber(McmComponentContainer.MessagesChannelName, UIThreadSynchronizationMode.PostAsynchronousInUIThread)]
private void handleAsyncOperationRequestMessage(AsyncOperationRequestMessage m)
{
    executeButton.Enabled = false;
    outputTextBox.Text = "Working...";
}

[MessageSubscriber(McmComponentContainer.MessagesChannelName, UIThreadSynchronizationMode.PostAsynchronousInUIThread)]
private void handleAsyncOperationResponseMessage(AsyncOperationResponseMessage m)
{
    executeButton.Enabled = true;
    outputTextBox.Text = string.Empty;
}

Reporting errors

Reporting errors in asynchronous operations could by implemented in many ways. In this simple case we show an error message directly on the screen if any error occures. Repeat the step creating the new MCM component just as you have made it creating CurrentOutputTextComponent. Name the new component ErrorPresenterComponent.

    [Export(typeof (McmComponent))]
    public class ErrorPresenterComponent : McmComponent
    {
        [MessageSubscriber(McmComponentContainer.MessagesChannelName, UIThreadSynchronizationMode.PostAsynchronousInUIThread)]
        private void handleAsyncOperationResponseMessage(AsyncOperationResponseMessage m)
        {
            if (m.HasError)
            {
                MessageBox.Show(m.Error.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
For testing just extend ReverseTextCommand.ExecuteCore() method with the following check.

if (string.IsNullOrEmpty(Argument.InputText))
{
    throw new InvalidOperationException("Please define the input text.");
}

Debugging the application

Debugging an asynchronous application can be very hard. Tracing the order and content of the broadcasted messages could be of a great help. To provide this functionality create an additional MCM component presenting all messages broadcasted in the system. For more detailed view the messages can override their ToString() method.

[Export(typeof (McmComponent))]
public class DebuggingComponent : McmComponent
{
    [MessageSubscriber(McmComponentContainer.MessagesChannelName)]
    private void handleEveryMessage(object m)
    {
        Debug.WriteLine(m.ToString());
    }
}
Please note, that creating new components does not change the program architecture in any case. Modularity is one of the biggest advantages of MCM.

Download the hello world source
Watch video


Last edited Mar 3, 2013 at 9:14 PM by polo, version 4

Comments

No comments yet.