RTTI and TScreen to the rescue!
A much better approach is to handle the "got focus" and "lost focus" situations on a higher level.The TScreen class exposes the OnActiveControlChange event immediately after input focus changes to a new windowed control. This event is fired by the global Screen object that all Delphi applications can exploit, by default. There's no need to handle each and every control's OnEnter and OnExit event. OnActiveControlChange is fired when the input focus shifts within the active form, and across forms to a new form when it becomes the active form. Great!
To get around the limitation of having to check the type of the control to type cast to the correct class in order to be able to access the Color property, you can reach for Delphi's RTTI.
By using Delphi's run-time type information you can check if a control exposes a particular property as public, get or set the new value - all this without worrying about the class of the control.
For a start, you need to define a value for the background color of the currently active (with the input focus) control. You also need to have two variables to hold the reference to the previously active control and its background color.
Add the following declarations to the interface section of your application's Main form:
constSince there is no TScreen component you can drop on a form, you need to attach the event handler for the OnActiveControlChange event manually. The Main form's OnCreate event handler is the best place. You also need to make sure that you clean up the screen object when the form is freed by detaching the event handler from the event in the main form's OnDestroy event handler:
focusColor = clSkyBlue;
var
lastFocused : TWinControl;
originalColor : TColor;
procedure TMainForm.FormCreate(Sender: TObject) ;Here's the implementation of the "ScreenActiveControlChange" procedure.
begin
Screen.OnActiveControlChange := ScreenActiveControlChange;
end;
procedure TMainForm.FormDestroy(Sender: TObject) ;
begin
Screen.OnActiveControlChange := nil;
end;
procedure TMainForm.ScreenActiveControlChange(Sender: TObject) ;When the OnActiveControlChange is fired by the Screen object, the ScreenActiveControlChange is executed. Here's what we want to happen:
var
doEnter, doExit : boolean;
previousActiveControl : TWinControl;
begin
if Screen.ActiveControl = nil then
begin
lastFocused := nil;
Exit;
end;
doEnter := true;
doExit := true;
//CheckBox
if Screen.ActiveControl is TButtonControl then doEnter := false;
previousActiveControl := lastFocused;
if previousActiveControl <> nil then
begin
//CheckBox
if previousActiveControl is TButtonControl then doExit := false;
end;
lastFocused := Screen.ActiveControl;
if doExit then ExitColor(previousActiveControl) ;
if doEnter then EnterColor(lastFocused) ;
end;
- The "previousActiveControl" variable holds the reference to the control that had the input focus "last time".
- The ActiveControl property (TWinControl type) of the Screen object Indicates which control currently has input focus on the screen (any of the forms in your project). Therefore, we first need to make sure the ActiveControl is assigned.
- Some controls, like TCheckBox, do not look nice if we change their background color. The "doEnter" and "doExit" variables are used to ensure that we really want to change the background color on the active control (and restore on the last focused). The TCheckBox derives from a more generic TButtonControl - which encapsulates behavior common to button controls, check boxes, and radio buttons. In short, we do not want to change the back color of such controls.
- Finally, a custom "ExitColor" procedure is called passing the reference to the control that had the input focus prior to OnactiveControlCnahge being raised. Naturally, "EnterColor" is called to change the background color of the currently focused control.


