If you have been writing your own DotNetNuke modules for any amount of time you have probably run across the concept of Inter-Module Communication (IMC for short). IMC particularly refers to the facility that the DotNetNuke framework provides to allow data to be passed between modules during the ASP.Net page life cycle. It is a very elegant solution for passing data between modules, on the server side. That said, it tends to be a topic that most first-time IMC users have trouble getting right. The documentation is thin, and there aren’t too many examples out there. So in this article we will be tackling this subject as well as taking a look at a few examples.
In my follow-up post I want to discuss alternative ways for modules to communicate. There are numerous ways to pass data between modules, both on the server and on the client side.We will see how to use JavaScript, DNN Event Messaging, Bubbling Events, Control Nesting, the DNN Client API, ASP Sessions, and the Query String, all to communicate between modules.
That is a lot to cover, so let’s get started!
DotNetNuke Inter-Module Communication (IMC)
As general as the term “Inter-Module Communication” may sound, when DNN module developers use the term IMC they are specifically referring to the facility within the DNN framework which allows you to use VB.Net or C# in your code-behind in order to pass information between modules. IMC communication always occurs somewhere within the ASP.Net page life cycle. Therefore this method of passing data between modules requires exactly one execution of the page life cycle. If user interaction is required to trigger the communication, then a post-back will need to occur from the browser to trigger the page life cycle in which the IMC will take place.
How IMC Works
IMC functionality is divided into two parts: Communicating (sending messages) and Listening (receiving messages). In order to add one or both of these functionalities to a modules, you must have the module class implement the respective IMC interfaces which the DNN Framework has defined for you.
The IModuleCommunicator and IModuleListener Interfaces
Both IModuleCommunicator and IModuleListener live inside the DotNetNuke.Entities.Modules.Communications namespace, so you may find it convenient to reference these namespaces at the top of your code. You will find that I have included such references throughout my examples.
Implementing the IModuleCommunicator interface allows your module to dispatch messages to all the modules on the page that are listening for IMC messages. On the flip side of the equation, implementing the IModuleListener interface allows your module to receive all the messages sent from modules on the same page.
Implementing IModuleCommunicator
To implement IModuleCommunicator all you need to do is define an instance of the ModuleCommunicationEvenHandler delegate as an event named ModuleCommunication within your class.
C# Example:
using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;
public partial class SampleCommunicatorCS :
PortalModuleBase, IModuleCommunicator
{
public event ModuleCommunicationEventHandler ModuleCommunication;
}
VB Example:
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications
Partial Class SampleCommunicatorVB
Inherits PortalModuleBase
Implements IModuleCommunicator
Public Event ModuleCommunication(ByVal sender As Object, _
ByVal e As ModuleCommunicationEventArgs) _
Implements IModuleCommunicator.ModuleCommunication
End Class
When you implement the IModuleCommunicator interface, the DotNetNuke framework knows that your module has an instance of the ModuleCommunicationEventHandler delegate type named ModuleCommunication. This guarantees that the DotNetNuke core can wire-up to your module so that it can be notified when you raise an IMC communication event. Somewhere deep in the DNN core code, there is likely a line that adds an event handler to your delegate if your module implements IModuleCommunicator:
if (yourModuleInstance is IModuleCommunicator)
{
yourModuleInstance.ModuleCommunication +=
new ModuleCommunicationEventHandler(notifyIMCListeners);
}
By doing this, the DNN core can respond to every ModuleCommunication event that your module raises.
You Raise a ModuleCommunication Event to Send an IMC Message
Sending an IMC message is as simple as raising your module's ModuleCommunication event. In C# you simply call ModuleCommunication(...), passing to it a reference to your module, and an instance of ModuleCommunicationEventArgs.
ModuleCommunication(this, mcArgs);
To raise the ModuleCommunication event in VB.Net, you use the RaiseEvent keyword in addition to calling ModuleCommunication(...), passing to it a reference to your module, and an instance of ModuleCommunicationEventArgs.
RaiseEvent ModuleCommunication(Me, mcArgs)
Creating the Event Args Object
When you raise the ModuleCommunication event you are required to pass in an instance of the ModuleCommunicationEventArgsDotNetNuke class which is found in the Entities.Modules.Communications namespace. This class is like an envelope that you use to send IMC messages out to other modules. You need to instantiate a copy and set its properties before you pass it to the ModuleCommunication call.
C# Example:
// the IMC message data gets stored inside
// a ModuleCommunicationEventArgs object
ModuleCommunicationEventArgs mcArgs =
new ModuleCommunicationEventArgs();
mcArgs.Sender = "SampleCommunicatorModule";
mcArgs.Target = "Arbitrary Text";
mcArgs.Text = "Your payload text";
mcArgs.Type = "Your custom type";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path/to/xml/doc.xml");
mcArgs.Value = xmlDoc;
// if ModuleCommunication is null,
// the cache settings for your module
// might need to be set to 0 (turned off)
if (ModuleCommunication != null)
{
// calling your ModuleCommunication delegate event
// will cause the event to be raised
ModuleCommunication(this, mcArgs);
}
VB Example:
Dim mcArgs As ModuleCommunicationEventArgs _
= New ModuleCommunicationEventArgs()
mcArgs.Sender = "SampleCommunicatorModule - VB"
mcArgs.Target = "Arbitrary Text"
mcArgs.Text = "Your Payload Text"
mcArgs.Type = "Your Custom Type"
Dim xmlDoc As System.Xml.XmlDocument = New System.Xml.XmlDocument()
xmlDoc.Load("path/to/xml/doc.xml")
mcArgs.Value = xmlDoc
RaiseEvent ModuleCommunication(Me, mcArgs)
All of the ModuleCommunicationEventArgs properties can be used any way you see fit. It is important to remember that every IMC message that your module sends out will be received by every module that implements IModuleListener. There are no built-in mechanisms for directing IMC messages to particular modules. Therefore, it is up to the module developer to establish their own consistent use of the ModuleCommunicationEventArgs properties in order to identify the messages that a particular module needs to listen for.
The Sender, Target, Text, and Type properties of the ModuleCommunicationEventArgs class are of type String. The Value property is of type object. In the example above I set the Value property to an instance of XmlDocument just to demonstrate that you can pass types other than String in this property.
Implementing IModuleListener
In order for your module to receive IMC messages it must implement the IModuleListener interface. This interface requires your class to have a method named OnModuleCommunication which takes two parameters, and Object type and a ModuleCommunicationEventArgs type, respectively.
C# Example:
using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;
public partial class SampleListenerCS :
PortalModuleBase, IModuleListener
{
public void OnModuleCommunication(object s,
ModuleCommunicationEventArgs e)
{
throw new NotImplementedException();
}
}
VB Example:
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications
Partial Class SampleListenerVB
Inherits PortalModuleBase
Implements IModuleListener
Sub OnModuleCommunication(ByVal s As Object, _
ByVal e As ModuleCommunicationEventArgs) _
Implements IModuleListener.OnModuleCommunication
End Sub
End Class
Your OnModuleCommunication Method Executes Every Time Any Module Raises a ModuleCommunication Event
When you implement the IModuleListener interface the DotNetNuke framework automatically adds your OnModuleCommunication method to the delegate that responds to all the ModuleCommunication events. When your OnModuleCommunication method is executed, it is passed a reference to the module that raised the ModuleCommunication even, this is the first argument of your OnModuleCommunication method. Your method also gets passed the ModuleCommunicationEventArgs object that contains all the information that was defined by the object that raised the event.
ModuleCommunicationEventArgs is Key
The ModuleCommunicationEventArgs objec is the key piece of the IMC framework because it is the single channel in which the communication happens. Anything placed into the ModuleCommunicationEventArgs object from inside the module that raises the ModuleCommunication event gets passed along to every OnModuleCommunication method of the modules that implement the IModuleListener interface.
Full Examples For Your Dissecting Pleasures - C#
Communicator XML File in C# - SampleCommunicatorCS.ascx:
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="SampleCommunicatorCS.ascx.cs"
Inherits="SampleCommunicatorCS" %>
Communicator Code-Behind File in C# - SampleCommunicatorCS.ascx.cs:
using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;
using System.Xml;
public partial class SampleCommunicatorCS :
PortalModuleBase, IModuleCommunicator
{
public event ModuleCommunicationEventHandler ModuleCommunication;
protected void Page_Load(object sender, EventArgs e)
{
// the IMC message data gets stored inside
// a ModuleCommunicationEventArgs object
ModuleCommunicationEventArgs mcArgs =
new ModuleCommunicationEventArgs();
mcArgs.Sender = "SampleCommunicatorModule";
mcArgs.Target = "Arbitrary Text";
mcArgs.Text = "Your payload text";
mcArgs.Type = "Your custom type";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path/to/xml/doc.xml");
mcArgs.Value = xmlDoc;
// if ModuleCommunication is null,
// the cache settings for your module
// might need to be set to 0 (turned off)
if (ModuleCommunication != null)
{
// calling your ModuleCommunication delegate event
// will cause the event to be raised
ModuleCommunication(this, mcArgs);
}
}
}
Listener XML File in C# - SampleListenerCS.ascx:
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="SampleListenerCS.ascx.cs"
Inherits="SampleListenerCS" %>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
Listener Code-Behind in C# - SampleListenerCS.ascx.cs:
using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;
public partial class SampleListenerCS :
PortalModuleBase, IModuleListener
{
public void OnModuleCommunication(object s,
ModuleCommunicationEventArgs e)
{
if (e.Target == "Pizza Inspectors")
{
if(e.Text.ToLower().Contains("garlic")
{
Label1.Text = "Garlic Detected In Pizza";
}
}
}
}
Full Examples For Your Dissecting Pleasures - VB
Communicator XML File in VB - SampleCommunicatorVB.ascx
<%@ Control Language="VB" AutoEventWireup="true"
CodeFile="SampleCommunicatorVB.ascx.vb"
Inherits="SampleCommunicatorVB" %>
Communicator Code-Behind in VB - SampleCommunicatorVB.ascx.vb
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications
Partial Class SampleCommunicatorVB
Inherits PortalModuleBase
Implements IModuleCommunicator
Public Event ModuleCommunication(ByVal sender As Object, _
ByVal e As ModuleCommunicationEventArgs) _
Implements IModuleCommunicator.ModuleCommunication
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
Dim mcArgs As ModuleCommunicationEventArgs _
= New ModuleCommunicationEventArgs()
mcArgs.Sender = "SampleCommunicatorModule - VB"
mcArgs.Target = "Arbitrary Text"
mcArgs.Text = "Your Payload Text"
mcArgs.Type = "Your Custom Type"
Dim xmlDoc As System.Xml.XmlDocument = New System.Xml.XmlDocument()
xmlDoc.Load("path/to/xml/doc.xml")
mcArgs.Value = xmlDoc
RaiseEvent ModuleCommunication(Me, mcArgs)
End Sub
End Class
Listener XML File in VB - SampleListenerVB.ascx
<%@ Control Language="VB" AutoEventWireup="false"
CodeFile="SampleListenerVB.ascx.vb"
Inherits="SampleListenerVB" %>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
Listener Code-Behind in VB - SampleListenerVB.ascx.vb
Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications
Partial Class SampleListenerVB
Inherits PortalModuleBase
Implements IModuleListener
Sub OnModuleCommunication(ByVal s As Object, _
ByVal e As ModuleCommunicationEventArgs) _
Implements IModuleListener.OnModuleCommunication
If e.Target = "Pizza Inspectors" Then
If e.Text.ToLower.Contains("garlic") Then
Label1.Text = "Garlic Detected In Pizza!"
End If
End If
End Sub
End Class