RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Build an AJAX Content Management System with Visual WebGUI: Creating a Framework : Page 4

Flexible software systems require a foundation that supplies the basic system services that's easy for developers to use, yet powerful enough to support complex and flexible systems—in other words, a framework.


Designing a Flexible Event-Based Infrastructure

To facilitate flexible communication between modules, the example uses two delegated and corresponding generic EventArgs classes, which let you pass any data between modules. Figure 4 shows the EventArgs class and its delegates.

Figure 4. Generic Events: Here's the class definition for the generic event arguments.

The WTGenericEventHandler delegate is designed to work in conjunction with WTGenericEventArgs, while the WTCancelGenericEventHandler works with WTCancelGenericEvent.

The WTGenericEventArgs class lets you add custom, arbitrary data, which makes it easy to exchange data between objects that fire events and objects that subscribe to those events. Table 4 shows the class's properties and methods.

Table 4. WTGenericEventArgs Properties and Methods: You can use this class to pass arbitrary data between objects during event-handling.
Property Description
string EventType
  { get; set; }
This is as an optional identifier or name for the event. Event handlers can decide what to do with the event data based on this value. For example, an event dispatcher that monitors events from multiple components can use the EventType to determine which target components should receive events.
bool IsEventDataAvailable
  { get; }
Returns true if the event argument has any custom data available (e.g. the Hashtable implementing the EventData member is not null; it contains some objects.)
Method Description
void AddEventData(
  string key,
  object data)
Add some key/value data to the event argument. The data can be retrieved later in the event handler.
void RemoveEventData(
  string key)
Removes data with the specified key from the event argument.
void ClearEventData() Clears all event data.
bool CheckEventDataExists(
  string dataKey)
Detemines whether event data exists for a specific key.

The WTGenericEventArgs class implements an indexer property so you can set and read event data easily, using the following code:

// . . .
WTCancelGenericEventArgs evt = new WTCancelGenericEventArgs();
evt["Name"] = textName.Text;
evt["Age"] = textAge.Text;
evt["City"] = textCity.Text;
string userName = evt["Name"];         // return the name 
// if data for a certain key was not set
// returns  null
string userCountry = evt["Country"];   // userCountry is null

It also implements the IEnumerable interface, so you can use foreach to retrieve all the data associated with an event.

WTCancelGenericEventArgs derives from WTGenericEventArgs, and implements a Cancel property. The IWTModule Close event handler is of type WTCancelGenericEventHandler. In addition to sending event data from the called to the calling module, it allows the calling module to detect whether the called module was closed with Cancel. That's convenient, so it does not have to refresh, and can perform additional tasks to handle the Cancel situation.

Playing with Workplaces and Modules

You've seen how to build a sample application that uses workplaces and modules. The main form holds two workplaces; one appears on the left and one on the right side of the form. The left workplace loads a Dashboard module, which can launch other modules that then appear either in the right-hand workplace, or replace the Dashboard in the left-hand workplace. Figure 5 and Figure 6 show examples.

Figure 5. Sample Application: Here's the sample application with the Dashboard module loaded into the left workplace, and a browser module loaded into the right workplace.
Figure 6. Sample Application with Data Entry Module: In this view of the sample application, you can see the browser module in the right-hand workplace, and a Data Entry module (launched from the Dashboard) loaded into the left-hand workplace.

Using the WTWorkplace and WTModule classes is simple. The workplaces in the sample application are named mainPanel (the left workspace) and workPanel (the right workspace). The Dashboard module derives from WTModule, and adds one new property, TargetWorkplace, which initially holds a reference to the right-hand workplace, workPanel.

Here's the code that loads the Dashboard into the main form:

private void Form1_Load(object sender, EventArgs e) {
    Dashboard d = new Dashboard();
    mainPanel.LoadModule(d, null);
    d.TargetWorkplace = workPanel;

Now the dashboard can load any of the modules either in its own workplace (replacing itself), or in the right workplace. For example:

// Dashboard loads Browser module in right workpanel 
TargetWorkplace.LoadModule(new BrowserModule(), this);
// . . .
// Dashboard loads data entry module in own workpanel 
HostWorkplace.LoadModule(new ModuleName(), this);

When a called module needs to close, it calls the CloseModule(…) method. If it needs to pass some data back to its calling module, it can be pass that data via the WTCancelGenericEventArg object that CloseModule gets as an argument, as in the following code:

// in called module, in the event handler for Save button
private void buttonSave_Click(object sender, EventArgs e) {
    WTCancelGenericEventArgs evt = new WTCancelGenericEventArgs();
    // add data to pass back to calling module
    Evt["FirstName"]= textFirstName.Text;
    Evt["LastnName"]= textLastName.Text;
    Evt["Age"]= textAge.Text;

The calling module can access the data by key or by using foreach to iterate through all the data fields:

// . . .
// in caller module, the code for Refresh method
public override void Refresh(WTCancelGenericEventArgs e){
    // if cancel is true, the called module was closed with cancel button
    if (e.Cancel)
        statusBar1.Text = "Name was canceled";
    else {
        ContactBO contact = new ContactBO();
        Contact.FirstName = e[“FirstName”];
        Contact.LastName = e[“LastName”];
        statusBar1.Text = 
            string.Format("Welcome, {0}", 

The downloadable code contains sample solutions for both Visual Studio 2005 and 2008. You just need to set Web.Config to the correct version for your development platform.

Bogdan Zamfir has a Master's degree in Computer Science and has been working as an independent consultant and software developer since 1993. He develops both web and desktop applications, using various languages and platforms including C#, VB.NET, Visual FoxPro, Microsoft Access, Visual Basic, and C/C++. Visit his web site for more information.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date