set TEdit.PasswordChar 属性 in TEdit.onEnter event causes TEdit cannot show selection and cursor in Delphi XE seattle

set TEdit.PasswordChar property in TEdit.onEnter event causes TEdit can not show selection and cursor in Delphi XE seattle

我在窗体上放置了一个 TEdit 控件作为密码。我的目标是,让 TEdit 控件在未获得焦点时显示星号,以避免其他人看到它,并在获得焦点时显示允许编辑的密码。

为此,我添加了 OnEnter 和 OnExit 处理程序以相应地将 PasswordChar 属性 设置为 #0 和 '*'。

这在 Delphi 7 中运行良好,但在 Delphi XE seattle 中出错,TEdit 控件进入了一个奇怪的状态,它没有显示闪烁的编辑光标,也没有显示突出显示的触发 OnEnter 事件后的蓝色背景选择(其中 PasswordChar 设置为 #0)。

我在调试模式下进入TEdit控件源代码,发现TEdit控件在设置PasswordChar时会重新创建它的一些句柄(我不太了解Windows API)。我猜这是导致问题的原因,但不确定,也不知道如何避免。

请帮我解决这个问题,我试了很多都没有找到办法。

这里是最简单的测试程序:

type
  TTestForm = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Edit1Enter(Sender: TObject);
    procedure Edit1Exit(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  TestForm: TTestForm;

implementation

{$R *.dfm}

procedure TTestForm.Edit1Enter(Sender: TObject);
begin
  Edit1.PasswordChar := #0;
end;

procedure TTestForm.Edit1Exit(Sender: TObject);
begin
  Edit1.PasswordChar := '*';
end;

end.

dfm:

object TestForm: TTestForm
  Left = 0
  Top = 0
  Caption = 'TestForm'
  ClientHeight = 242
  ClientWidth = 472
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Edit1: TEdit
    Left = 72
    Top = 48
    Width = 225
    Height = 21
    PasswordChar = '*'
    TabOrder = 0
    Text = 'You can not see text selection'
    OnEnter = Edit1Enter
    OnExit = Edit1Exit
  end
  object Edit2: TEdit
    Left = 72
    Top = 75
    Width = 225
    Height = 21
    TabOrder = 1
    Text = 'Click this control to trigger TEdit1.OnExit'
  end
end

您可以使用 EM_SETPASSWORDCHAR 消息清除 ES_PASSWORD 样式。这使编辑保持正常状态。但随后在 OnExit 中,您需要设置 passwordchar 两次,以便进行编辑以重新创建其 window。或者您可以使用一些 hack 控件来访问 RecreateWnd 过程(因此它不会创建 window 两次)。

procedure TTestForm.Edit1Enter(Sender: TObject);
begin
  SendMessage(Edit1.Handle, EM_SETPASSWORDCHAR, 0, 0);
end;

procedure TTestForm.Edit1Exit(Sender: TObject);
begin
  Edit1.PasswordChar := #0;
  Edit1.PasswordChar := '*';
end;