Figure A: This is the example program using TOLabel.
![[ Figure A ]](cpb9953a.gif)
All we have to do to finish TOLabel is add the two mouse-aware events and publish those properties and events that we want users of TOLabel component to have access to.
So, how do we know when the mouse enters TOLabel's space and when it leaves that space? Inprise has again done most of the work for us. The VCL sends us two messages, CM_MOUSEENTER and CM_MOUSELEAVE. Can you guess what they do?
We need to catch these two messages. We can do this with a message map:
BEGIN_MESSAGE_MAP MESSAGE_HANDLER(CM_MOUSEENTER, TMessage, CMMouseEnter) MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage, CMMouseLeave) END_MESSAGE_MAP(TCustomLabel)
When TOLabel receives the messages CM_MOUSEENTER and CM_MOUSELEAVE, the message map will call the functions CMMouseEnter() and CMMouseLeave(), respectively. These two functions simply call a user-supplied event, if one exists, passing the address of the TOLabel object that received the message:
void __fastcall TOLabel::CMMouseEnter(
Messages::TMessage &Message)
{
if (FOnMouseEnter) FOnMouseEnter(
this);
}
void __fastcall TOLabel::CMMouseLeave(
Messages::TMessage &Message)
{
if (FOnMouseLeave) FOnMouseLeave(
this);
}
FOnMouseEnter and FOnMouseLeave are data members of TOLabel that hold the user-assigned functions for the events. The user assigns the values to FOnMouseEnter and FOnMouseLeave through two properties published by TOLabel:
__property TNotifyEvent OnMouseEnter =
{read = FOnMouseEnter, write =
FOnMouseEnter};
__property TNotifyEvent OnMouseLeave =
{read = FOnMouseLeave, write =
FOnMouseLeave};
Just to be safe, we should set FOnMouseEnter and FOMouseLeave to null in the constructor of TOLabel:
__fastcall TOLabel::TOlabel(TComponent*
Owner) : TCustomLabel(Owner)
{
FOnMouseEnter = 0;
FOnMouseLeave = 0;
}
Finally, we finish TOLabel by publishing the normal properties of a TLabel component. These are declared as protected in TCstomLabel; we need to declare them as published in TOLabel:
__published: __property Align; // etc. ...
All the code for TOLabel can be found in Listings A and B.
Listing A: TOLabel include
#ifndef OLabelH
#define OLabelH
#include <SysUtils.hpp>
#include <Controls.hpp>
#include <Classes.hpp>
#include <Forms.hpp>
#include <StdCtrls.hpp>
class PACKAGE TOLabel : public TCustomLabel
{
private:
TNotifyEvent FOnMouseEnter;
TNotifyEvent FOnMouseLeave;
protected:
void __fastcall CMMouseEnter(Messages::TMessage
&Message);
void __fastcall CMMouseLeave(Messages::TMessage
&Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CM_MOUSEENTER, TMessage,
CMMouseEnter)
MESSAGE_HANDLER(CM_MOUSELEAVE, TMessage,
CMMouseLeave)
END_MESSAGE_MAP(TCustomLabel)
public:
__fastcall TOLabel(TComponent* Owner);
__published:
__property TNotifyEvent OnMouseEnter = {read =
FOnMouseEnter, write = FOnMouseEnter};
__property TNotifyEvent OnMouseLeave = {read =
FOnMouseLeave, write = FOnMouseLeave};
__property Align;
__property Alignment;
__property AutoSize;
__property Caption;
__property Color;
__property DragCursor;
__property DragMode;
__property Enabled;
__property FocusControl;
__property Font;
__property ParentColor;
__property ParentFont;
__property ParentShowHint;
__property PopupMenu;
__property ShowAccelChar;
__property ShowHint;
__property Transparent;
__property Layout;
__property Visible;
__property WordWrap;
__property OnClick;
__property OnDblClick;
__property OnDragDrop;
__property OnDragOver;
__property OnEndDrag;
__property OnMouseDown;
__property OnMouseMove;
__property OnMouseUp;
__property OnStartDrag;
};
#endif
Listing B: TOLabel source
#include <vcl.h>
#pragma hdrstop
#include "OLabel.h"
#pragma package(smart_init)
static inline void ValidCtrCheck(TOLabel *)
{
new TOLabel(NULL);
}
// ---------------------------------------------------------------
__fastcall TOLabel::TOLabel(TComponent* Owner) :
TCustomLabel(Owner)
{
FOnMouseEnter = 0;
FOnMouseLeave = 0;
}
void __fastcall TOLabel::CMMouseEnter(Messages::TMessage &Message)
{
if (FOnMouseEnter) FOnMouseEnter(this);
}
void __fastcall TOLabel::CMMouseLeave(Messages::TMessage &Message)
{
if (FOnMouseLeave) FOnMouseLeave(this);
}
// ---------------------------------------------------------------
namespace Olabel
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TOLabel)};
RegisterComponents("ZDJ", classes, 0);
}
}
As a finishing touch, you can add a custom bitmap to represent TOLabel in the component palette of the IDE. You can use a bitmap of your own or use the one included with the source code for TOLabel.
Each item on the left, a vegetable or meat, is an instance of TOLabel. When the mouse cursor passes over an item, the item's color changes from black to red and the Category of the item, Vegetable or Meat, is displayed by a TPanel object on the right. When the mouse cursor leaves an item, the item's color is set back to black and the Category displayed on the right is cleared.
If you click on one of the items, a message box will announce your selection. You can see how this is done in Listings C and D for the example program.
Listing C: Example program header
#ifndef MainH
#define MainH
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "OLabel.h"
#include <ExtCtrls.hpp>
class TMainForm : public TForm
{
__published:
TPanel *Category;
TOLabel *OLabel;
void __fastcall OLabelMouseEnter(TObject *Sender);
void __fastcall OLabelMouseLeave(TObject *Sender);
void __fastcall OLabelClick(TObject *Sender);
public:
__fastcall TMainForm(TComponent* Owner);
};
extern PACKAGE TMainForm *MainForm;
#endif
Listing D: Example program source
#include <vcl.h>
#pragma hdrstop
#include "Main.h"
#pragma package(smart_init)
#pragma link "OLabel"
#pragma resource "*.dfm"
TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner)
{
}
void __fastcall TMainForm::OLabelMouseEnter(TObject *Sender)
{
TOLabel *label = dynamic_cast<TOLabel *>(Sender);
if (!label) return;
label->Font->Color = clRed;
Category->Caption = label->Tag == 1 ? "Meat" : "Vegetable";
}
void __fastcall TMainForm::OLabelMouseLeave(TObject *Sender)
{
TOLabel *label = dynamic_cast<TOLabel *>(Sender);
if (!label) return;
label->Font->Color = clBtnText;
Category->Caption = "";
}
void __fastcall TMainForm::OLabelClick(TObject *Sender)
{
TOLabel *label = dynamic_cast<TOLabel *>(Sender);
if (!label) return;
Application->MessageBox(label->Caption.c_str(), "You clicked
on...", MB_OK);
}
TMainForm, the form in our example program, has three functions that are all assigned to different events of our TOLabel objects. OLabelMouseEnter() is assigned to the OnMouseEnter event, OLabelMouseLeave() to OnMouseLeave, and OLabelClick() is assigned to--you guessed it--OnClick.
Notice that these three functions are used for all nine instances of TOLabel. This is much more efficient than writing a separate function for each instance. Let's look at how these functions work.
First, test all three functions to make sure that the Sender argument passed to them is indeed a pointer to a TOLabel object. We do this by testing the result of a dynamic cast:
TOLabel *label = dynamic_cast<TOLabel *>(Sender); If (!label) return;
If, for some reason, Sender isn't a pointer to a TOLabel, the functions just return without doing anything. Next, OLabelMouseEnter() and OLabelMouseLeave() change the color of the label font:
label->Font->Color = clRed;
and
label->Font->Color = clBtnText;
OLabelMouseEnter() sets the Caption property of the Category TPanel to Vegetable or Meat based on the Tag property of the TOLabel object. During the design of the form, we set the value of Tag to 0 for vegetables and 1 for meats:
Category->Caption = label->Tag == 1 ? "Meat" : "Vegetable";
OLabelMouseLeave() simply clears the Caption property of the Category TPanel:
Category->Caption = "";
OLabelClick() displays a message box showing the Caption of the TOLabel that was clicked:
Application->MessageBox(label->Caption.c_str(), "You clicked on...", MB_OK);