如何覆盖调用以在TEdit中显示CapsLock提示窗口?

Nas*_*out 6 delphi delphi-10-seattle

基本上我有这个问题:TEdit中的CapsLock密码消息在视觉上无法使用VCL样式.

我想要做的不是解决问题,如答案或评论中所示.

我想完全禁用那个丑陋的提示窗口.而是显示一个图像,让用户知道帽子被锁定.

像这样

在此输入图像描述

Nas*_*out 7

我找到了我的问题的解决方案,它涉及到我宁愿不使用的黑客攻击.

它是这样的.

  1. 覆盖WndProc.

type
  TEdit = class (Vcl.StdCtrls.TEdit)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;
Run Code Online (Sandbox Code Playgroud)
  1. 拦截EM_SHOWBALLOONTIP消息,你就完成了

procedure TEdit.WndProc(var Message: TMessage);
begin
 if Message.Msg = EM_SHOWBALLOONTIP then
   showmessage('Do your thing.')
 else
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请查看MSDN文档:

如何在密码编辑控件上禁止CapsLock警告?


这是TEdit的后代,如果FOnPasswordCaps指定了某些事件,它将允许在密码编辑控件上禁止CapsLock警告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.
Run Code Online (Sandbox Code Playgroud)

Kobik先生制作了一段非常优雅的代码,我认为不应该信任PasteBin,所以我决定在这里添加它.

根据我的理解,它允许您处理TPasswordCapsLockState一个事件处理程序,该事件处理程序在TPasswordEdit接收焦点时失效,失去焦点,在焦点上按下CapsLock键并在PasswordChar更改时触发可选触发.

使用这种方法,我可以使用OnPasswordCapsLock事件在我的问题中显示/隐藏图像,而不是强迫组件的使用者为每个状态使用两个事件处理程序(顺便说一句非常聪明且不易出错).

也只要LNeedHandle := FBlockCapsBalloonTip and IsPassword;True我还有一个附加功能,TPasswordEdit它是处理OnEnterOnExitOnPasswordCapsLock和,

那么我能说什么Kobik Je vous轮胎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;
Run Code Online (Sandbox Code Playgroud)

  • 这只是您问题解决方案的一半.您可以使用它来触发图标的显示.但是,当用户切换大写锁定时,编辑控件将关闭工具提示时没有相应的通知.因此,您必须自己跟踪Caps Lock的状态以隐藏您的图标.因此,您也可以使用它来显示图标,并使用此解决方案来解除编辑的工具提示. (2认同)