Handle Messages

This page describes how to do event-based development in the Application Forms framework.

Contents

Overview

This page describes the application logic for taking action when an event occurs. An event can be when a user clicks a push button, opens a menu or saves a record. It can also be that a window is loaded, activated, resized or about to be destroyed. There is a vast amount of different events that is launched from the operating system, .NET or the Application Forms framework. This page is describing events that are received by the application through a Win32 API message.

The Application Forms framework was first built on Centura Team Developer, which is a 4GL language built around Windows' messages. There is no easy way to batch convert the whole framework logic into C# events which is the main reason why messages still play an important role for Application Forms functionality and likely will do so for a long time to come. 

Choosing the right message

The Application Forms framework is built upon IceTea (tm) PPJ framework, which in turn is built on C# .NET. The .NET framework is based on the operating system native and legacy Win32 API's. When developing with Application Forms framework it is possible to use messages and events that origin from these different abstraction layers. It's not always easy to know which event or message to make use of. The recommended guideline is to use Application Forms and PPJ events and messages when available, and C# .Net events when there is no equivalent to be found. Win32 messages should be avoided and only be used in those rare occasions when there is no other way. Conforming to these guidelines makes debugging easier, improves maintainability and improves chances for compatibility between different versions of the layers.

Application Forms messages

Messages in Application Forms make use of and extends Windows' messages. The Application Forms framework is heavily based on the original framework written in Centura Team Developer. CTD supported and made use of multiple inheritance and it was also used throughout the IFS framework. The updated C# Application Forms framework is still based on this original design, but in C# multiple inheritance is not supported. Multiple inheritance is implemented using aggregates. To be able to send and receive messages between the aggregated classes and to keep the order of invocation, the framework needs to extend the standard WndProc method. In Application Forms, messages are consumed by subscribing to the WindowActions event of a window or control. This is described further in Receive a Message.

The WindowActions event encapsulates messages that are possible to receive in APF applications and messages sent by the application. The following message categories are available for development:

Note: Apart from these types, there are also messages prefixed AM_ (Application Messages). They are not to be used by APF applications, due to that they are reserved for the internal framework logic. The framework does not guarantee compatibility for an AM_ message between releases.

 Messages can be divided into two logical groups:

Method invocation messages are sent by an application or the framework to invoke methods, such as methods for populating data sources, creating new records etc. Notification messages are sent by the framework to inform the application that an event has occurred, e.g. events like an object got focus or a keyboard key was hit.

Declare an Application Message

Application Messages are messages declared in an application to be used within the APF application or between APF applications. The framework declares a constant PAM_User, which is a message range base constant for Application Messages.

This constant can be used by APF applications to define Application Message constants. Figure 1 shows how the Application Message constant PAM_DoX is declared as PAM_User + 1.

public sealed class Const
{
/// <summary>
/// Constants in My component
/// </summary>
const int PAM_DoX = Ifs.Fnd.ApplicationForms.Const.PAM_User + 1;
const int PAM_DataXChanged = Ifs.Fnd.ApplicationForms.Const.PAM_User + 2;

Figure 1: Declaration of Application Messages based on the PAM_User constant.

Note: Declaring Application Message constants to be used between applications can cause collisions when the same constant range has already been used in the receiving component for something else. You should pay a little extra attention to this when declaring or making use of Application Message constants where two APF applications interact.

Receive a message

When working with messages in Application Forms the normal WndProc method should not be used. Every form and control in the Application Forms framework that publishes messages do this through the WindowActions event. To handle messages, do the following:

  1. Create an event handler in a window or control to receive messages.
  2. In the property dialog connect the WindowActions event to the event handler created.
  3. Add a switch statement to select the message to process.
  4. Write the application logic.
  5. Mark message as handled, set return value if applicable and exit the message handler.

Create a message handler

A message handler can be written from scratch, inserted as a Window Actions Code Snippet or partly generated by the designer. To get the handler to be generated by the designer, open the designer and select the control to generate a handler for. In the Property Dialog double click the WindowActions event and the handler is partly generated for you. Message handlers are recommended to be placed within the Window Actions region.

In Figure 2 the SAM_Create logic is placed outside the WindowActions event handler, in the method frmCompany_OnSAM_Create. This is something that is recommended to do in most cases. The Window Actions call back method tend to contain multiple events and if the logic is kept within that method the source will be difficult to read and maintain.

#region frmCompany Actions
 
/// <summary>
/// Window Actions for frmCompany
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmCompany_WindowActions(object sender, WindowActionsEventArgs e)
{
	#region Actions
	switch (e.ActionType)
	{
		case Sys.SAM_Create:
			this.frmCompany_OnSAM_Create(sender, e);
			break;
	}
	#endregion
}
 
/// <summary>
/// SAM_Create event handler.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmCompany_OnSAM_Create(object sender, WindowActionsEventArgs e)
{
	#region Actions
	e.Handled = true;
	e.Return = Sal.SendClassMessage(Sys.SAM_Create, e.WParam, e.LParam);
	// TODO: Implement the handler here.
	#endregion
}
 
#endregion

Figure 2: WindowActions event handler for frmCompany

Tip: When using the code snippet template to insert a message handler, the template sets "frm" as default value for the control name. Change this value and tab away from the field and the template will fill in your changed values for all the placeholders in the template. Same goes for the message constant that has default value "PM_".

Processing a message

When processing a received message, the information inside can be taken into account in order to achieve the desired functionality. The WindowActions event callback method takes a WindowActionsEventArgs as parameter. This parameter carries detailed information about the message and can be analyzed to control behavior.

Message Code

The property ActionType of a WindowActionsEventArgs contains the message's ID. In the message handler, implement a switch statement to select code to execute based on the property ActionType. Implement a case for each message type you need to act on and a message handler method, where the application performs the actions for an event.

Message parameters

All messages comes with two properties called WParam and LParam. These parameters are always present for a message, but are not always used. You should read the message specific documentation for information on how the parameters are used. These parameters can be accessed either trough the static references Sys.wParam and Sys.lParam or trough the WindowActionsEventArgs properties WParam and LParam.

Return value

As for parameters, all the messages has a return value that is always present, but not always used. You set the return value by accessing the Return property of the WindowActionsEventArgs. In Figure 3 the return property is assigned the return value of the call. This is a typical implementation where the application implementation passes on the return value to the framework.

Send class message

Many messages are used both by the application and the framework. To make sure that you do not shortcut framework logic you need to pass on the message to the framework. To pass on a new instance of the received message, call Sal.SendClassMessage.

e.Return = Sal.SendClassMessage(Sys.SAM_Create, e.WParam, e.LParam);

Figure 3: Extraction from Figure 2 that highlights the class message call.

Message handled

When processing a message it is important to remember to set the Handled property. This property is used by the framework to optimize the message dispatch logic. When a message is dispatched and returns without the handled property set, then the framework assumes that no party is dependent of that message type and will not dispatch it again.

e.Handled = true;

Figure 4: Event handlers should set the processed messages to handled to indicate to the framework that the message type is in use.

Send a message

Sometimes a window object in an application needs to initiate actions in other objects. To achieve this, a message can be sent to other window objects. The message to send can either be a framework message or a custom defined message. The framework methods Sal.SendMsg, Sal.PostMsg, and Sal.SendMsgToChildren are used to send a message. A message is either posted or sent: