Using MemBus for messaging between application components

Sometimes we need publisher/subscriber messaging in our applications to broadcast messages to different parts of system in real time. We can always build our own solution for this but we can also use something that is already there. In this posting I will show you how to use MemBus to send messages from MDI parent form to MDI child forms.

NB!  You can download and browse source code of this post from my GitHub samples repository.

What is MemBus?

As we can read from MemBus introduction:

“it is messaging framework … that utilizes the semantics of sending and receiving messages. Those messages are not meant to leave the AppDomain where the Bus lives in. There is no durability – when the AppDomain is gone, the Bus is gone.”

So it seems like we have interface to send and receive messages between our system components. Now let’s build something to demonstrate how to use MemBus for something useful.

Design draft

Before going to details let’s take a look at my design draft. This is just drawing that gives you idea about logical parts of system we are building. How we implement our application is another topic. But this is the concept we will try to follow.

MemBus forms draft

We need something that receives events and lets MDI parent window know that there is new data. MDI parent window constructs message and sends it to Bus. Bus has subscribers that are implemented as MDI child windows. These windows implementIObserver interface. When new child window is opened then it is also registered as subscriber to bus. When child window is closed then it is unregistered. So, there is nothing complex.

Sample application

Before sending and receiving messages we need some type. Well, string is the simplest one but let’s write some more interesting application that we can extend to cool demo. So, instead of simple types I will use my own class called GeoLocationItem. The class is here.


public class GeoLocationItem
{
   
public string Title { get; set
; }
   
public DateTime Time { get; set
; }
   
public decimal Latitude { get; set
; }
   
public decimal Longitude { get; set; }
}

Now we need event receiver. It is possible to build some more complex receiver but let’s try to make something more primitive that doesn’t need much code and maintenance. I will use Timer object on my MDI parent form. After every three seconds it sends new message to bus.


private void LocationTimerTick(object sender, EventArgs e)
{
   
var item = new GeoLocationItem
();
    item.Time = DateTime.Now;
 
   
var
secondString = item.Time.Second.ToString();
    item.Title =
"Car " + secondString[secondString.Length - 1];
    item.Longitude = item.Time.Second;
    item.Latitude = item.Time.Millisecond;
 
    _bus.Publish(item);
}

This code creates GeoLocationItem with some random data and it simulates how to track cars on their tracks.

We have also IBus instance defined in form scope.


public partial class MainForm : Form
{
   
private readonly
IBus _bus;
 
   
public
MainForm()
    {
        InitializeComponent();
 
        _bus =
BusSetup
.StartWith<Fast>().Construct();
    }
 
   
// more code here
}

When new MDI child window is opened then it is registered also with bus. When child window is closed it is also removed from bus so it does not receive events anymore.


private void NewWindowToolStripMenuItemClick(object sender, EventArgs e)
{
   
var child = new ChildForm {MdiParent = this
};
 
   
var observable = _bus.Observe<GeoLocationItem
>();
    observable.Subscribe(child);
 
    child.Tag = observable;           
    child.FormClosed += ChildFormClosed;
    child.Show();
}
 

static void ChildFormClosed(object sender, FormClosedEventArgs
e)
{
   
var form = (Form
)sender;
   
var observable = form.Tag as IObservable<GeoLocationItem
>;
   
if (observable != null
)
    {
        observable.Subscribe(
null);
    }
}

The tricky part here is how form is introduced to bus. I ask new observable object from bus and then register my child window as subscriber to it. When child window is closed then I subscribe null as observer and bus stops sending messages to this instance. If you don’t unsubscribe then form is not destroyed and receives messages background. You may not want it.

Here is the child form. It implements IObserver interface and when new messages is received then it sends out event that is invoked in form’s own thread. Otherwise we get errors and there is no feedback shown on child forms.


public partial class ChildForm : Form, IObserver<GeoLocationItem>
{
   
private delegate void AddDataItemDelegate(GeoLocationItem
item);
   
private readonly AddDataItemDelegate
_addDataItem;
 
   
public
ChildForm()
    {
        InitializeComponent();
 
        _addDataItem =
new AddDataItemDelegate
(SetValue);
    }
 
   
public void OnNext(GeoLocationItem
value)
    {
        Invoke(_addDataItem,
new object
[] { value });
    }
 
   
private void SetValue(GeoLocationItem
value)
    {
       
var item = new ListViewItem
();
        item.Text = value.Time.ToString();
 
        item.SubItems.Add(value.Title);
        item.SubItems.Add(value.Latitude.ToString());
        item.SubItems.Add(value.Longitude.ToString());
 
        BusDataList.Items.Insert(0, item);
    }
 
   
public void OnError(Exception
error)
    {
       
throw new NotImplementedException
();
    }
 
   
public void OnCompleted()
    {
 
    }
}

Now let’s try to run our program.

And here is the result

I opened some windows and tiled them in MDI parent. Here is the example of program.

MemBus forms application

Okay, that’s it for now. You can see that all my eight child forms are receiving messages from parent through MemBus and this was our goal.

Conclusion

Although we can create our own messaging solutions where some clients are publishers and other are subscribers of messages we can avoid this task and use existing solutions. In this posting I demonstrated how to use MemBus to organize communication between MDI parent and child forms. We used nice standardized interface without any bad hacks and we got clean and working solution.


Leave a Reply

Your email address will not be published. Required fields are marked *