I found the solution to my problem, It involves a hack that I would rather not use.
It goes like this.
- Override WndProc.
code
type
TEdit = class (Vcl.StdCtrls.TEdit)
protected
procedure WndProc(var Message: TMessage); override;
end;
- Intercept the
EM_SHOWBALLOONTIP
message and you are done
code
procedure TEdit.WndProc(var Message: TMessage);
begin
if Message.Msg = EM_SHOWBALLOONTIP then
showmessage('Do your thing.')
else
inherited;
end;
For more information check the MSDN documentation:
How do I suppress the CapsLock warning on password edit controls?
This is a descendant of TEdit that would allow to suppress the CapsLock warning on password edit controls, if a certain FOnPasswordCaps
events are assigned with PasswordChar <> #0
unit NCREditUnit;
interface
uses
Vcl.StdCtrls,
vcl.Controls,
Winapi.Messages,
System.Classes;
type
TNCREdit = class(TEdit)
private
FOnPasswordCapsLocked: TNotifyEvent;
FIsCapsLocked: boolean;
FOnPasswordCapsFreed: TNotifyEvent;
FBlockCapsBalloonTip: boolean;
FValuePasswordChrOnCaps: boolean;
procedure SetOnPasswordCapsEvents;
procedure SetOnPasswordCapsFreed(const aValue: TNotifyEvent);
procedure SetOnPasswordCapsLocked(const aValue: TNotifyEvent);
protected
procedure WndProc(var Message: TMessage); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure DoEnter; override;
procedure DoExit; override;
published
property BlockCapsBalloonTip: boolean read FBlockCapsBalloonTip write FBlockCapsBalloonTip default False;
property ValuePasswordChrOnCaps: boolean read FValuePasswordChrOnCaps write FValuePasswordChrOnCaps default True;
//... The usual property declaration of TEdit
property OnPasswordCapsLocked: TNotifyEvent read FOnPasswordCapsLocked write SetOnPasswordCapsLocked;
property OnPasswordCapsFreed: TNotifyEvent read FOnPasswordCapsFreed write SetOnPasswordCapsFreed;
end;
implementation
uses
Winapi.CommCtrl,
Winapi.Windows;
{ TNCREdit }
procedure TNCREdit.DoEnter;
begin
inherited;
if FBlockCapsBalloonTip then
begin
FIsCapsLocked := Odd(GetKeyState(VK_CAPITAL));
SetOnPasswordCapsEvents;
end;
end;
procedure TNCREdit.DoExit;
begin
if FBlockCapsBalloonTip and (FIsCapsLocked) then
begin
FIsCapsLocked := False;
SetOnPasswordCapsEvents;
end;
inherited;
end;
procedure TNCREdit.KeyUp(var Key: Word; Shift: TShiftState);
begin
if Key = VK_CAPITAL then
FIsCapsLocked := not FIsCapsLocked;
SetOnPasswordCapsEvents;
inherited;
end;
procedure TNCREdit.SetOnPasswordCapsEvents;
begin
if FIsCapsLocked then
begin
if Assigned(FOnPasswordCapsLocked) and
((self.PasswordChar <> #0) or ( not FValuePasswordChrOnCaps)) then
begin
FOnPasswordCapsLocked(Self);
end;
end
else
begin
if Assigned(FOnPasswordCapsLocked) and
((self.PasswordChar <> #0) or ( not FValuePasswordChrOnCaps)) then
begin
FOnPasswordCapsFreed(Self);
end;
end;
end;
procedure TNCREdit.SetOnPasswordCapsFreed(const aValue: TNotifyEvent);
begin
FOnPasswordCapsFreed := aValue;
FBlockCapsBalloonTip := True;
end;
procedure TNCREdit.SetOnPasswordCapsLocked(const aValue: TNotifyEvent);
begin
FOnPasswordCapsLocked := aValue;
FBlockCapsBalloonTip := True;
end;
procedure TNCREdit.WndProc(var Message: TMessage);
begin
if (Message.Msg = EM_SHOWBALLOONTIP) and FBlockCapsBalloonTip then Exit;
inherited;
end;
end.
Mr Kobik made a very elegant piece of code that I think PasteBin should not be trusted to host, so I decided to add it here.
From what I understood it lets you handle TPasswordCapsLockState
in one event handler that is fired when the TPasswordEdit
receives focus, loses focus, CapsLock key pressed while on focus and an optional firing when PasswordChar
is changed.
Using this approach I could use the OnPasswordCapsLock
event to show/hide the image in my question instead of forcing the consumer of the component to use two event handlers for each state (very clever by the way and less error prone).
also as long as LNeedHandle := FBlockCapsBalloonTip and IsPassword;
is True
I have another added feature to TPasswordEdit
which is the handling of OnEnter
and OnExit
in OnPasswordCapsLock
as well,
So what can I say Mr Kobik Je vous tire mon chapeau.
type
TPasswordCapsLockState = (pcsEnter, pcsExit, pcsKey, pcsSetPasswordChar);
TPasswordCapsLockEvent = procedure(Sender: TObject;
Locked: Boolean; State: TPasswordCapsLockState) of object;
TPasswordEdit = class(TCustomEdit)
private
FIsCapsLocked: boolean;
FBlockCapsBalloonTip: boolean;
FOnPasswordCapsLock: TPasswordCapsLockEvent;
protected
procedure WndProc(var Message: TMessage); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure DoEnter; override;
procedure DoExit; override;
procedure HandlePasswordCapsLock(State: TPasswordCapsLockState); virtual;
function GetIsPassword: Boolean; virtual;
public
property IsPassword: Boolean read GetIsPassword;
published
property BlockCapsBalloonTip: boolean read FBlockCapsBalloonTip write FBlockCapsBalloonTip default False;
//... The usual property declaration of TEdit
property OnPasswordCapsLock: TPasswordCapsLockEvent read FOnPasswordCapsLock write FOnPasswordCapsLock;
end;
implementation
function TPasswordEdit.GetIsPassword: Boolean;
begin
Result := ((PasswordChar <> #0) or
// Edit control can have ES_PASSWORD style with PasswordChar == #0
// if it was creaed with ES_PASSWORD style
(HandleAllocated and (GetWindowLong(Handle, GWL_STYLE) and ES_PASSWORD <> 0)));
end;
procedure TPasswordEdit.HandlePasswordCapsLock;
var
LNeedHandle: Boolean;
begin
LNeedHandle := FBlockCapsBalloonTip and IsPassword;
if LNeedHandle then
begin
FIsCapsLocked := Odd(GetKeyState(VK_CAPITAL));
if Assigned(FOnPasswordCapsLock) then
FOnPasswordCapsLock(Self, FIsCapsLocked, State);
end;
end;
procedure TPasswordEdit.DoEnter;
begin
inherited;
HandlePasswordCapsLock(pcsEnter);
end;
procedure TPasswordEdit.DoExit;
begin
inherited;
HandlePasswordCapsLock(pcsExit);
end;
procedure TPasswordEdit.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited;
if Key = VK_CAPITAL then
HandlePasswordCapsLock(pcsKey);
end;
procedure TPasswordEdit.WndProc(var Message: TMessage);
begin
if (Message.Msg = EM_SHOWBALLOONTIP) and FBlockCapsBalloonTip and IsPassword then
Exit;
// Optional - if password char was changed
if (Message.Msg = EM_SETPASSWORDCHAR) and Self.Focused then
HandlePasswordCapsLock(pcsSetPasswordChar);
inherited;
end;