This guide will walk through the process of writing a reaction for Mayhem. There are a many similarities between this and the guide for creating an event.

The reaction we are going to create is a popup where the text of the popup is configurable.

Set up a new VS C# class library project for this event. This guide walks through that process: Setting Up Visual Studio To Write Modules

Create a new class called Popup and open it up. Add the following attribute to the class:

[DataContract]
[MayhemModule("Popup Window", "Creates a popup window")] 

Mayhem looks for classes denoted with the MayhemModule attribute to know it is a module that can be used. All events and reactions you write will need to have that attribute specified. The first string is the title of your module in the module lists and what shows up on the main Mayhem window. The second string is a brief explanation of what the module does.

We have to add a reference to System.Runtime.Serialization for the DataContract attribute. We need that attribute to be able to serialize some module settings.

Now extend the class ReactionBase from MayhemCore. Basic reactions do not need to extend a lot of methods from ReactionBase. All that is required is that the method Perform() is overridden.

We want to show a popup window, so we need to reference the .NET library System.Windows.Forms.

Then make the contents of the Perform method be:

MessageBox.Show("Hello World");

We now need to build the class library we have created and add it to Mayhem. So build your project, and locate the dll you created in your project’s bin folder.

Locate the main Mayhem directory, if you compiled the Mayhem source itself, then that is the top-most bin folder. If you installed Mayhem from the web, that is %ProgramFiles%/Outercurve/Mayhem. Just paste your dll into that folder.

Now open Mayhem and you will see Popup Window as an item in your Reaction list.

Making our popup text configurable

Our reaction is extremely simple right now, we aren’t using any logic, and don’t have anything configurable. We are going to now modify our reaction so that we can change what the contents of the popup text are. This text will persist when users close and re-open the application, and we will show it on the main screen so that users can tell what the text will be without opening the configuration menu.

Let’s start by making a private variable on our class of type string called message. This will be the user specified text.

Change Perform to show our message variable instead of “Hello World”.

Since the text will be user configurable, we want to be able to show the message text on the main Mayhem application screen. To do this, we have to tell Mayhem our reaction is configurable by implementing the IWpfConfigurable interface from MayhemWpf.

In order to set that configuration string we have to implement the method GetConfigString(). The implementation of this method should return some string that provides a look into what the configuration settings are for the reaction.

Now, in order to make the module save the configured state when the application is shut down and restarted, we need to add a little bit of data contract code.

If you didn't set up your class following our guide, go ahead and add a reference to your project to the .NET library System.Runtime.Serialization. Also add the attribute [DataContract] to your Popup class. This tells .NET that you have state to save.

Now we need to mark our configurable variables as well. So add the [DataMember] attribute to your message variable.

That’s it! Now when that variable is modified and the application is shut down, it will come back with that value intact. Now we just need to have a way to let the user modify that variable with a configuration window.

The Configuration Window

In order to make the configuration window, add a new WPF UserControl item to the project named PopupConfig.xaml. Replace the contents of the xaml file with:

<src:WpfConfiguration x:Class="TestModules.PopupConfig"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:src="clr-namespace:MayhemWpf.UserControls;assembly=MayhemWpf"
             Width="400">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="300" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
 
        <TextBlock Style="{StaticResource ConfigLabel}" Grid.Column="0" Grid.Row="0">Message:</TextBlock>
        <TextBox Name="MessageBox" Grid.Column="1" Grid.Row="0" />
     </Grid>
</src:WpfConfiguration>

This is the most basic of configuration windows. It simply shows a textbox that a user can type text into. We will also going to end up doing validation of the message whenever it is changed. WpfConfiguration purely handles the display of how the module asks for information. As you hopefully notice, it doesn’t contain the save button, or the title of the window. All that is handled for you to maintain consistency between configuration windows. We also provide default styles that you can use to further maintain consistency and user expectation.

We now need to create the backing code that makes the configuration window come together. Change PopupConfig.xaml.cs to extend the base class WpfConfiguration instead of the default UserControl base class.

In the constructor we should ask for all the information we need to populate the config window with the current settings. That means we need to know the current message string. Add a string parameter to the constructor called message. Also add a public property on the class with a private setter called Message. In the constructor, set this property with the parameter we were given.

Your class should now look like this:

  using System.Windows;
  using System.Windows.Controls;
  using MayhemWpf.UserControls;
 
  namespace TestModules
  {
     public partial class PopupConfig : WpfConfiguration
     {
         public string Message
         { 
             get; 
             private set;
         }
  
         public PopupConfig(string message)
         {
             this.Message = message;
             InitializeComponent();
         }
     }
 }

WpfConfiguration gives us some handy hooks that we can use. We need to set the text of the textbox, which we either could have done with databindings, but we will be setting it in code here. Override the OnLoad() method and make its contents:

MessageBox.Text = this.Message; 

You don't need a call to base.OnLoad().

We also need to set the title that shows up on the configuration window. To do this override the Title property. Have it return “Popup”.

We now need to go back to the Popup class and implement those configuration related methods. The property ConfigurationControl just wants you to return an instance of your control. Change the contents of the getter to

  return new PopupConfig(message);

Notice that we passing it the current value of the Message property.

OnSaved is a method called after the configuration control has been saved. We need to grab the new message and set it on our class. To be able to do that, we need to cast the configurationControl to the type of our user control, and then get the public Message property we created. That means that the contents of OnSaved is:

  message = ((PopupConfig)configurationControl).Message;

If you compile now and put it in the mayhem folder as we did for the first version you will notice that our popup module will now ask to be configured when you choose it in the reaction list. But you can’t hit save! Let’s solve that.

We will eventually do validation of the message text when it is changed, but for now let’s just enable the Save button.

WpfConfiguration has a property called CanSave. This property sets whether the Save button is enabled or disabled. For now let’s just always have the save button enabled.

In the PopupConfig constructor, add CanSave = true;

Compile and run your module inside of mayhem again and you will notice that you can now save your configuration. Make a connection using your module. Notice that you now see the message you specified as the text shown on the face of the module view. If you go back into the configuration, you can change that value and it will be reflected there. If you shut down the application and start it up again, that module still has the correct message configured.

Let’s now go back and make the configuration window validate the message entered.

Change your xaml file to have the contents:

    <src:WpfConfiguration x:Class="TestModules.PopupConfig"
        xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:MayhemWpf.UserControls;assembly=MayhemWpf"
        Width="400">
        <Grid>
            <Grid.ColumnDefinitions>
               <ColumnDefinition Width="100" />
               <ColumnDefinition Width="300" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
               <RowDefinition />
               <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Style="{StaticResource ConfigLabel}" Grid.Column="0" Grid.Row="0">Message:</TextBlock>
            <TextBox Name="MessageBox" Grid.Column="1" Grid.Row="0" TextChanged="MessageText_TextChanged" />
            <TextBlock Style="{StaticResource ConfigErrorMessage}" Name="textInvalid" Grid.Column="1" Grid.Row="1">Message is invalid</TextBlock>
        </Grid>
    </src:WpfConfiguration>

We added a new row to the grid with a TextBlock with an invalid text message. We also added a TextChanged event to the MessageBox.

Let’s add that event handler to our backing code.

    private void MessageText_TextChanged(object sender, TextChangedEventArgs e)
    {
        Message = MessageBox.Text.Trim();
        CanSave = Message.Length > 0;
        textInvalid.Visibility = CanSave ? Visibility.Collapsed : Visibility.Visible;
    }

We are setting our property to the current entered text in the MessageBox, and then enabling the CanSave button only if the length of the string is greater than 0. Let’s remove CanSave = true from the constructor now.

We then show or hide the error message depending on whether you can save or not. If you run this, it works great and all the functionality is there. However, the user experience is not as good as it could be. When you open the configuration for the first time and the string is empty, you see the error message. You should only see the error message after you start typing.

Add a new field to your PopupConfig class:

    private bool shouldCheckValidity = false; 

In your OnLoad method, after you set the text on the MessageBox, set that variable to true.

Now in the MessageText_TextChanged event handler, wrap it’s contents in

    if (shouldCheckValidity)

If you compile and run your code inside of Mayhem, you will see that the experience is smooth now and that you have a fully working reaction module that lets you configure and save text that will show up in a MessageBox.

Now take what you have learned, and build something amazing!

Last edited Mar 14, 2012 at 6:31 AM by TheSavior, version 16

Comments

ChrisK91 Jun 9, 2012 at 5:00 PM 
I get a message, that the resource ConfigLabel could not be resolved. Where is it located?

noscgag Mar 13, 2012 at 11:09 PM 
Could you post the source code to this sample project?