Home

VCL Topics:
Controls Messages and Events

 

Overview

An application is made of various objects or controls. During the lifetime of the application, its controls regularly send messages to the operating system to do something on their behalf. These messages must be processed appropriately. Also, most of the time, more than one application is running on the computer. The controls of such an application also send messages to the operating system. As the operating system is constantly asked to perform these assignments, because there can be so many requests presented unpredictably, the operating system leaves it up to the controls to specify what they want, when they want it, and what behavior or result they expect.

A message, like a letter you send to somebody, must provide a few pieces of information in order to be processed. For a Win32 application, these pieces of information are stored in a structure called MSG. It is defined as follows:

typedef struct tagMSG {
	HWND hwnd; 
	UINT message; 
	WPARAM wParam; 
	LPARAM lParam; 
	DWORD time; 
	POINT pt; 
} MSG, *PMSG;

For a Win32 application, each message is processed by a function called a window procedure. A window procedure is a pointer to function, therefore declared as CALLBACK, and it returns a positive 32-bit number. Therefore, a MSG variable must be passed to the window procedure for processing.

In a Win32 application, a window procedure that processes a message requires 3 pieces of information with the last piece divided in two (which produces 4 pieces of information):

  • The first piece of information must state WHO, that is, what control, sent the message
  • The second argument must specify the name of the message as each Windows message is recognized by a name, which is simply a positive but constant numeric value
  • The third and the fourth arguments carry information that depends on the message being sent

Here is an example:

//---------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg,
WPARAM wParam, LPARAM lParam)
{
	switch(Msg)
	{
		case WM_DESTROY:
			PostQuitMessage(WM_QUIT);
			break;
		default:
			return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}
//---------------------------------------------------------------------------

Once again, to make programming a fast process, the VCL provides its own message structure called TMessage and defined as follows:

struct TMessage
{
	Cardinal Msg;
	union
	{
		struct 
		{
			Word WParamLo;
			Word WParamHi;
			Word LParamLo;
			Word LParamHi;
			Word ResultLo;
			Word ResultHi;
		};
		struct 
		{
			int WParam;
			int LParam;
			int Result;
		};
	};
};

A TMessage message must provide two pieces of information, as the TMessage structure shows: the name of the message and one group of information. Unlike the Win32 MSG structure, in a VCL application, the object that sends the message is already known (because it is the one that sent the message). The accompanying items of the message are coded into either the top or the bottom structures. The two anonymous structures inside the union indicate that you use either the top or the bottom structure but not both.

Once a control has composed a message, it must send it to the right target, which could be the operating system. In order to send a message, a control must create an event. The control is also said to fire an event. To make a distinction between the two, a message's name will usually be written in uppercase. An example would be WM_MOVE, which stands for Window Message Move. The name of an event usually starts with On, which indicates an action.

Remember, the message is what needs to be sent. The event is the action of sending the message.

A Map of Messages

As mentioned already, the messages of a Win32 application are processed by a window procedure. For a VCL application, you can use the same approach. Alternatively, you can use a technique referred to as creating a map of messages.

For the compiler to manage messages, they should be listed in a public section of the class definition. The messages are included in a list referred to as a message map. The list of messages, that is, the message map, starts with the BEGIN_MESSAGE_MAP and ends with the END_MESSAGE_MAP macros. The END_MESSAGE_MAP macro takes an argument, which is the original class that holds the primary implementation of the messages.

Between the BEGIN_MESSAGE_MAP and the END_MESSAGE_MAP macros, each message is declared using either the MESSAGE_HANDLER or the VCL_MESSAGE_HANDLER macros. Their syntaxes are:

MESSAGE_HANDLER(VCLMessageName, TMessage, EventName);
VCL_MESSAGE_HANDLER(WindowsMsgName, VCLMessageName, EventName);

There are various categories of messages the operating system receives. Some of them come from the keyboard, some from the mouse, and some others from various other sources. For example, some messages are sent by the application itself while some other messages are controlled by the operating system.

Messages Characteristics

The most commonly sent messages have already been coded in the objects of the VCL controls so much that you will hardly need to define new messages, at least not in the beginning of your C++ Builder programming adventure. Most of what you will do consists of implementing the desired behavior when a particular message is sent.

Therefore, to start, you should know what messages are available, when, and how they work. As mentioned already, each control sends its own messages when necessary. Based on this, some messages are unique to some controls according to their roles. Some other messages are common to various controls, as they tend to provide similar actions. To manage such various configurations, the VCL considers the messages in two broad categories. Those that take an argument and those that do not. The VCL defines events as function pointers (or pointers to function).

As it happens, some messages do not require much information to be performed. For example, suppose your heart sends a message to the arm and states, “Raise your hand”. In this case, suppose everything is alright, the arm does not ask, “how do I raise my hand?”. It simply does. This type of message would be sent without any accompanying information. Consider another message where the arm carries some water and says to the mouth, “Swallow the following water”. The mouth would need the water that needs to be swallowed. Therefore, the message must be accompanied by additional information, which is considered an argument. Consider one more message where the heart says to the tongue, “Taste the following food but do not swallow it.” In order to process this message, the tongue would need the food and something to indicate that the food must not be swallowed. In this case, the message must be accompanied by two pieces of information.

To process messages that do not require any additional argument, the VCL creates such an event with the TNotifyEvent type. Such an event is declared as a pointer to function in the classes.hpp library as follows:

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);

The Sender argument is the control that is sending the messages.

Besides TNotifyEvent, the other events carry different and appropriate names.

Event Implementation

Although there are different ways you can implement an event, there are two main ways you can initiate its coding. If the control has a default event and if you double-click it, the compiler would initiate the default event. Another technique you can use is to click the Events tab of the Object Inspector. This would display a list of the events associated with the selected control:

The list is divided in two columns. The name of each event is displayed on the left side. You can click the name of an event to reveal a combo box. If a similar event has already been written, you can click the arrow of the combo box and select it from the list:

Similar events are those that share a behavior. Otherwise, to initiate an event double-click the field on the right column of the name of the desired event.

When an event has been initiated, you would be transported to the Code Editor and the cursor would be positioned in the body of the event, ready to receive your instructions. To customize an event, the compiler divides its structure in three sections:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
                                      int X, int Y)
{
	// Write code associated with the event here
}
//---------------------------------------------------------------------------

The coding of an event starts with its return value. Most or all events in C++ Builder return void. All events use the __fastcall convention.

The return type is followed by the name of the parent class from where the event would be fired. This is mainly the class that controls the form.

After the name of the class, the compiler rightly uses the class member access operator (::) to call the event, following a C++ rule.

The name of an event is made of a combination of the control that “owns” or fired the event and the name of the event.

Each event has at least one argument, the TObject *Sender. Some events use additional arguments that we will review when coding such events.

Keyboard Messages

 

Introduction

Keyboard

A keyboard is a hardware object attached to the computer. By default, it is used to enter recognizable symbols, letters, and other characters on a control. Each key on the keyboard displays a symbol, a letter, or a combination of those, to give an indication of what the key could be used for.

The user typically presses a key, which sends a signal to a program. The signal is analyzed to find its meaning. If the program or control that has focus is equipped to deal with the signal, it may produce the expected result. If the program or control cannot figure out what to do, it ignores the action.

Each key has a code that the operating system can recognize. This code is known as the virtual key code and they are as follows:

Virtual Key Used for Virtual Key Used for
VK_F1 F1 VK_F2 F2
VK_F3 F3 VK_F4 F4
VK_F5 F5 VK_F6 F6
VK_F7 F7 VK_F8 F8
VK_F9 F9 VK_F10 F10
VK_F11 F11 VK_F12 F12
VK_SCROLL Scroll Lock VK_SNAPSHOT Prt Scrn (Depends on keyboard)
VK_PAUSE Pause/Break VK_TAB Tab
VK_BACK Backspace VK_CAPITAL Caps Lock
VK_SHIFT Shift VK_CONTROL Ctrl
VK_MENU Alt VK_ESCAPE Escape
VK_RETURN Enter VK_SPACE Space Bar
VK_INSERT Insert VK_HOME Home
VK_PRIOR Page Up VK_DELETE Delete
VK_END End VK_NEXT Page Down
VK_UP Up Arrow Key VK_RIGHT Right Arrow Key
VK_DOWN Down Arrow Key VK_LEFT Left Arrow Key
VK_LWIN Left Windows Key VK_RWIN Right Windows Key
VK_APPS Applications Key    

The following keys apply to the Numeric Keypad

VK_NUMLOCK Num Lock    
VK_NUMPAD0 0 VK_NUMPAD1 1
VK_NUMPAD2 2 VK_NUMPAD3 3
VK_NUMPAD4 4 VK_NUMPAD5 5
VK_NUMPAD6 6 VK_NUMPAD7 7
VK_NUMPAD8 8 VK_NUMPAD9 9
VK_DIVIDE / VK_MULTIPLY *
VK_SUBTRACT - VK_ADD +
VK_SEPARATOR   VK_DECIMAL .

There are actually more keys than that but the above are the most frequently used.

The VCL implements keyboard events using two function pointers, TKeyEvent and TKeyPress that depend on the message.

Practical Learning Practical Learning: Introducing Keyboard Messages

  1. Start a new project with the default form
  2. Save it in a new folder called Keyboard1
  3. Save the Unit as Exercise and save the project as Keyboard

The Key Down Message

When a keyboard key is pressed, a message called WMKeyDown is sent. WMKeyDown is a TKeyEvent message that produces the OnKeyDown event. Its syntax is:

void __fastcall OnKeyDown(System::TObject* Sender, Word &Key,
                          Classes::TShiftState Shift);

The Key argument specifies the key that was pressed. The OnKeyDown() event can be sent by any key. Alphabetic keys are represented by their character. For example, if the user presses p, the Key argument would be represented as ‘p’. If the user presses a key, the Key argument would have the value of ‘;’.

The Shift argument specifies whether the Shift, the Ctrl, or the Alt keys were pressed along with the Key key. It is a Pascal Set whose enumerator has the following members:

Value Description
ssShift One of the Shift keys was pressed
ssAlt One of the Alt keys was pressed
ssCtrl One of the Ctrl keys was pressed

If the user presses two keys as an uppercase letter, such R, the Key argument would have a value of ‘r’. The Shift argument would be used to indicate that the Shift key was down when the letter r was pressed.

Practical Learning Practical Learning: Sending Key Down Messages

  1. To experiment with the WMKeyDown message, on the Object Inspector, click the Events tab
  2. Double-click the right field of OnKeyDown and implement the event as follows:
     
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
    TShiftState Shift)
    {
    	switch( Key )
    	{
    	case VK_RETURN:
    		ShowMessage("You pressed Enter");
    		break;
    	case VK_F1:
    		ShowMessage("Help is not available at the moment");
    		break;
    	case VK_DELETE:
    		ShowMessage("Can't Delete This");
    		break;
    	case 'K':
    		// If the user press Ctrl + K
    		if( Shift.Contains(ssCtrl) )
    			ShowMessage("K was pressed");
    		break;
    	}
    }
    //---------------------------------------------------------------------------
  3. Test the application and return to Bcb

The Key Up Message

As opposed to the key down message that is sent when a key is down, the WMKeyUp message is sent when the user releases the key. Like WMKeyDown, WMKeyUp is a TKeyEvent message. It produces the OnKeyUp event whose syntax is:

void __fastcall OnKeyUp(System::TObject* Sender, Word &Key,
Classes::TShiftState Shift);

The Key argument specifies the key that was pressed. Like the OnKeyDown event, the OnKeyUp event processes any key. Alphabetic keys are represented by their character. 

The Shift argument indicates whether the Shift, the Ctrl, or the Alt key participates in a key combination such as Shift + $

The Key Press Message

When the user presses a key, the WMKeyPress message is sent. Unlike the other two keyboard messages, the key pressed for this event should (must) be a character key. WMKeyPress produces the OnKeyPress event whose syntax is:

void __fastcall OnLKeyPress(System::TObject* Sender, char &Key);

The Key argument must be a letter or a recognizable symbol. Lowercase alphabetic characters, digits, and the lower base characters such as ; , ‘ [ ] - = / are recognized as they are. For an uppercase letter or an upper base symbols, the user must press Shift + the key. The character would be identified as one entity. This means that the symbol % typed with Shift + 5 is considered as one character.

Practical Learning Practical Learning: Sending Key Press Messages

  1. To experiment with the WMKeyPress message, on the Events tab of the Object Inspector, double-click the right field of OnKeyPress
  2. Delete the code in the OnKeyDown event and implement the OnKeyPress event as follows:
     
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
    {
    	switch( Key )
    	{
    	case VK_RETURN:
    		ShowMessage("You pressed Enter");
    		break;
    	case VK_F1:
    		ShowMessage("Help is not available at the moment");
    		break;
    	case VK_DELETE:
    		ShowMessage("Can't Delete This");
    		break;
    	case 'k':
    		ShowMessage("Lowercase k was pressed");
    		break;
    	case 'K':
    		ShowMessage("Uppercase K was pressed");
    		break;
    	case '$':
    		ShowMessage("Show me the money");
    		break;
    	}
    }
    //---------------------------------------------------------------------------
  3. Test the application and press the Delete or the F1 keys. Notice that nothing happens
  4. Press k
     
    Lowercase
  5. Click OK and press Shift + K
  6. Close the application and return Bcb

Mouse Messages

 

Introduction

The mouse is another object that is attached to the computer allowing the user to interact with the machine. The mouse and the keyboard can each accomplish some tasks that are not normally available on the other and both can accomplish some tasks the same way.

The mouse is equipped with two, three, or more buttons. When a mouse has two buttons, one is usually located on the left and the other is located on the right. When a mouse has three buttons, one is in the middle of the other two. The mouse is used to select a point or position on the screen. Once the user has located an item, which could also be an empty space, a letter or a word, he or she would position the mouse pointer on it. To actually use the mouse, the user would press the left, the middle (if any), or the right button. If the user presses the left button once, this action is called Click. If the user presses the right mouse button, the action is referred to as Right-Click. If the user presses the left button twice and very fast, the action is called Double-Click.

Mouse events are implemented as TMouseEvent and TMouseMoveEvent function pointers.

The Mouse Down Message

Imagine the user has located a position or an item on a document and presses one of the mouse buttons. While the button is pressed and is down, a button-down message is sent. The syntax of this event is as follows:

void __fastcall OnMouseDown(System::TObject* Sender, TMouseButton Button,
			    Classes::TShiftState Shift, int X, int Y);

The Button argument specifies what button was clicked. The buttons of the mouse are identified by the TMouseButton enumerator whose members are:

Value Description
mbLeft The left mouse button was clicked
mbRight The right mouse button was clicked
mbMiddle The middle mouse button was clicked

The Shift argument indicates whether mouse button and/or a keyboard key was/were pressed and held down when the Button was clicked. It can have one of the following values:

Value Description
ssShift One of the Shift keys was pressed
ssAlt One of the Alt keys was pressed
ssCtrl One of the Ctrl keys was pressed
ssLeft The left mouse button was held down
ssRight The right mouse button was held down
ssMiddle The middle mouse button was held down
ssDouble The Button was double-clicked

The X and the Y argument represent the TPoint(X, Y) point where the mouse was clicked.

Practical Learning Practical Learning: Sending a Mouse Down Message

  1. To experiment with the mouse down effect, on the Events tab of the Object Inspector, double-click the right side of the OnMouseDown field
  2. Implement the event as follows:
     
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
    				      TShiftState Shift, int X, int Y)
    {
    	ShowMessage("Mouse at (" + IntToStr(X) + ", " + IntToStr(Y) + ")");
    }
    //---------------------------------------------------------------------------
  3. Test the program and click various parts of the form
  4. Close it and return to Bcb

The Mouse-Up Message

After pressing a mouse button, the user usually releases it. While the button is being released, a button-up message is sent and it depends on the button, left or right, that was down. The event produced is OnMouseUp.

The OnMouseUp event uses the same syntax as the OnMouseDown event and processes the same arguments.

The Mouse Move Message

Whenever the mouse is positioned and being moved on top of a control, a mouse event is sent. This event is implemented as a TMouseMoveEvent function pointer and its syntax is:

void __fastcall OnMouseMove(System::TObject* Sender,
			    Classes::TShiftState Shift, int X, int Y);

The Shift argument is the same as the other mouse messages. The X and Y values identify the location of the mouse at the time this event fires.

Programmer Defined Messages

 

Introduction

The list of methods and messages available for the objects used in your applications is very impressive because the VCL tries to help process as many messages as possible. Unfortunately, in some areas, the VCL tends to address only the most commonly used messages, which is still very long, as we will see throughout this book.

The Win32 library provides more messages than the VCL implements. Normally, you should first check if a message you want to process is already available for your control. If it is not, you can create your own message and its event. Once again you have various options. If the message belongs to, or must be processed by, a form, you can create it in the header file of the form. Otherwise, you can create a new control by simply deriving a class from an existing control.

Windows Functions

Another way you will manipulate controls consists of calling a Win32 API function. There are two main categories of functions you will call: those that must identify the control that is calling the function and those that do not. If the function requires the control that called it, you can specify the control using its handle. If the function does not need this piece of information, then you can omit it. We will see various types of both categories of functions.

Most of the messages we will use in our applications are implemented in various classes of the VCL. Some others are not. Some of the messages are available in the Win32 library and you can use them in your application. This is made possible by calling the SendMessage() function. Its syntax is:

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

The hWnd argument is the object or control that is sending the message.

The Msg argument is the message to be sent.

The wParam and the lParam values depend on the message that is being sent.

The advantage of using the SendMessage() function is that, when sending this message, it would target the procedure that can perform the task and this function would return only after its message has been processed. Because this function can sometimes universally be used, that is by almost any control or object, the application cannot predict the type of message that SendMessage() is carrying. Therefore, (the probable disadvantage is that) you must know the (name or identity of the) message you are sending and you must provide accurate accompanying items.

Here is an example that changes the caption of a form using the WM_SETTEXT message:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
	const char *Msg = "This message was sent";
	SendMessage(Handle, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)Msg);
}
//---------------------------------------------------------------------------

Performing Messages

Besides the SendMessage() function, the TControl class provides the Perform() method that allows you to send a message any time. Its syntax is:

int __fastcall Perform(unsigned Msg, int WParam, int LParam);

The TControl::Perform() method functions like the SendMessage() function except that, since it is a VCL function, it omits the handle to the control because the control that calls it would be specified already. The first argument, Msg, is the identifier of the message that needs to be processed. It is exactly like the second argument of the SendMessage() function. Like the SendMessage() function, the values of WParam and LParam depend on the message. Here is an example used to close a form:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Perform(WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------

Custom Message Implementation

To customize an existing message of a control or to create a new message that is not available for your control, the TControl class provides the WndProc() method. Its syntax is:

virtual void __fastcall WndProc(Messages::TMessage &Message);

In order to use this method, you must override it in your own class. Here is an example:

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
public: // User declarations
	__fastcall TForm1(TComponent* Owner);
	virtual void __fastcall WndProc(TMessage &Msg);
};
//---------------------------------------------------------------------------

The Message argument is any message you want to process. As done for a Win32 application, the Message value is typically passed to a switch condition and each desired message is treated in a case statement. After processing the messages and before closing the member function, you should (must) call the parent class to process the messages that were not treated. Here is an example that treats the WM_MOVE message and prevents the user from moving the form (by preventing the mouse from capturing the title bar):

//---------------------------------------------------------------------------
void __fastcall TForm1::WndProc(TMessage &Message)
{
	switch(Message.Msg)
	{
		case WM_MOVE:
			ReleaseCapture();
		break;
	}

	TForm::WndProc(Msg);
}
//---------------------------------------------------------------------------

After creating the message, if it is intended for the form to treat, the message is ready and its event would fire appropriately. If the event is for a particular control, you must let the compiler know that you have a window procedure that would process a particular message or the messages of a certain control. To do this, you can assign WndProc to the control’s WindowProc member variable. This could be done in the constructor or the OnCreate() event of the form as follows:

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
	this->WindowProc = WndProc;
}
//---------------------------------------------------------------------------

Methods and Messages Combinations

The events of Windows controls are designed to be executed at specific periods in response to some occurrence. For example, only when the user presses a mouse button can a message related to this action would occur. Yet, one of the most influential ways of creating an effective program is to anticipate users actions as much as possible. This allows you to correctly respond. To support this idea, the VCL provides a system of combining controls messages and their associated events.

When the user initiates an action to send a message, the control that owns the action sends the message and an accompanying method. This method, although belonging to the control, allows other controls or other parts of the program to access the event as if they controlled when such an event would occur. For example, if the user clicks a control, the control composes and sends a click message. Because the control is also equipped with a Click() method, another control of the application can call its Click() event and perform the same action. Here is an example:

 

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Perform(WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Panel1MouseDown(TObject *Sender,
					TMouseButton Button,
                                        TShiftState Shift,
                                        int X,
                                        int Y)
{
	Button1->Click();
}
//---------------------------------------------------------------------------
Home Copyright © 2005 FunctionX, Inc.