Extensibility with Multiple Modules
It gets a little more complicated when you have more than one module tapping into this event. The code that executes the Invoke
method will fire all the modules wired to the CheckDataSource
events, one after the other. Remember, this happens because the initialization routine sends and receives the same ModuleEvents object to each module's Initialize method. Now think of the order of events here (no pun intended). If I have three modules that tap into this event, each checking the data source for something different, the first one that gets executed will receive the initial value of the Cancel
property, which is false
. If the code in any module changes that value, the next module that the code executes will contain that new value, because the Invoke
method gets called once with the one EventArgs object. This means that it is up to me to code the module event to check the value of e.Cancel
before executing any of the event logic. With that in mind, a properly written module that taps into the CheckDataSource
event should look like this:
' In VB:
Private Sub events_CheckDataSource( _
ByVal e As CheckDataSourceEventArgs)
If Not e.Cancel Then
If e.Source.ToUpper(). _
IndexOf("BAD") _> -1 Then
e.Cancel = True
// In C#:
IndexOf("BAD") > -1)
e.Cancel = true;
If you code all the modules this way, as soon as any module that intercepts this event sets the e.Cancel
property to true
, no other module's CheckDataSource
event will process any code.
Another choice for this kind of logic is probably a bit safer. The previous examples put the responsibility of check for cancellation in the module's code. But the same developer may not write all your modules, so it may not be reliable to do that. By writing a little more code on the client, you can iterate through the delegate invocation list (in this case, CheckDataSource
) and fire each one separately, checking the value of the Cancel
property after each one and deciding whether to continue.
' In VB:
Dim o_InvocationList() As _
[Delegate] = o_FilterEvents. _
For Each o_Filter As
Of CheckDataSourceEventArgs) In
If o_Filter IsNot Nothing Then
Dim o_EventArgs As New _
If o_EventArgs.Cancel Then
b_Cancel = True
// In C#:
Delegate o_InvocationList =
o_Filter in o_InvocationList)
if (o_Filter != null)
CheckDataSourceEventArgs o_EventArgs =
b_Cancel = true;
Using this technique, as soon as one module sets its e.Cancel
property to true, the code breaks out of the loop and stops further processing. In both techniques, the b_Cancel
variable determines whether processing should continue, but the responsibility for making the check differs.
You can insert additional extensibility points in either of the two methods: the one that calls all module events in one Invoke
execution or the one that iterates through the invocation list of the corresponding delegate and invokes one event at a time.
Extensibility modules are a great way to put multiple points of extensibility in your client and, at the same time, centralize how you write the extensibility plug-ins. You can choose to develop a class for each event you want to subscribe to, or you can group several together. If you choose the latter, you should do so because the interception code you're placing into the various events is somehow logically related from one event to another. An example of this would be in writing a Profanity Filter. You might want such a filter to check for profanity at different points in your application. For example, in the scenario you've seen, you could check for profanity at both the CheckDataSource
point or at one called PreProcessData
by writing one module class that taps into both of these events. The events will still be called from their appropriate extensibility points within the client, but they will be housed in one class, grouping them together logically. Listing 5
shows the entire code for such a module.
These patterns can help you design and code applications that are easier and more elegant to enhance or modify. They also introduce a style of programming that's based on the ability to interchange functionality easily. However, don't feel that you have to spend too much time on deciding where to insert points of extensibility in your application—just keep it all in mind. As you design and develop applications, more than likely you will find extensibility points that just snap out at you. Try to resist abstracting at the beginning of the design process, and don't be afraid to refactor into it later; but beware—after you get the hang of using these patterns, you will be addicted.
You can download the code for this article
from the User Group Downloads section of the link. You can find some basic references in the "Additional References
" sidebar. You can also watch a DNR-TV episode on this entire subject.