如何检查当前是否显示了SHAutoComplete()列表框?

时间:2012-02-10 07:57:15

标签: delphi winapi autocomplete

我正在使用Shell Lightweight Utility Functions中的SHAutoComplete()函数 库在模式对话框中为编辑字段启用路径自动完成。

按下 Esc 键时,对话框应该关闭,但仅当自动完成未激活时才会关闭。

如何检查焦点编辑控件当前是否显示完成列表?

修改

我在Windows XP 64上使用Delphi 2009. David发布的代码

procedure TMyForm.FormKeyDown(Sender: TObject; var Key: Word; 
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE then
    ModalResult := mrCancel;
end;

对我不起作用 - 对话框关闭。

2 个答案:

答案 0 :(得分:4)

我尝试了几个系统,结果很奇怪:

  • 在我的装有Windows XP 64的电脑上,当列表被删除时,对话框会关闭
  • 在VMware虚拟机中的Windows XP Pro上,对话框也关闭

  • 在我的笔记本电脑上使用Windows 7,对话框无法关闭
  • 在VMware虚拟机中的Windows 2000 Pro上,对话框未关闭

由于这是如此不稳定,我选择编写一个小组件,即使操作系统不提供也会强制执行正确的行为。


组件可以像这样使用:

procedure TForm2.FormCreate(Sender: TObject);
const
  SHACF_FILESYS_DIRS = $00000020;
begin
  SHAutoComplete(Edit1.Handle, SHACF_FILESYS_DIRS or SHACF_USETAB);
  fAutoSuggestDropdownChecker := TAutoSuggestDropdownChecker.Create(Self);
end;

procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE then begin
    if not fAutoSuggestDropdownChecker.DroppedDown then
      ModalResult := mrCancel;
  end;
end;

但是取消按钮没有设置Cancel属性非常重要。

组件本身通过挂钩应用程序消息处理并使用当前线程的窗口枚举来检查具有“Auto-Suggest Dropdown”类名称的可见窗口。如果存在且可见,则自动完成列表将被删除。

unit uAutoSuggestDropdownCheck;

interface

uses
  Windows, Classes, Messages, Forms;

type
  TAutoSuggestDropdownChecker = class(TComponent)
  private
    fDroppedDown: boolean;
    fSaveMessageEvent: TMessageEvent;
    procedure AppOnMessage(var AMsg: TMsg; var AHandled: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property DroppedDown: boolean read fDroppedDown;
  end;

implementation

////////////////////////////////////////////////////////////////////////////////

function EnumThreadWindowsProc(AWnd: HWND; AParam: LPARAM): BOOL; stdcall;
var
  WndClassName: string;
  FoundAndVisiblePtr: PInteger;
begin
  SetLength(WndClassName, 1024);
  GetClassName(AWnd, PChar(WndClassName), Length(WndClassName));
  WndClassName := PChar(WndClassName);
  if WndClassName = 'Auto-Suggest Dropdown' then begin
    FoundAndVisiblePtr := PInteger(AParam);
    FoundAndVisiblePtr^ := Ord(IsWindowVisible(AWnd));
    Result := False;
  end else
    Result := True;
end;

function IsAutoSuggestDropdownVisible: boolean;
var
  FoundAndVisible: integer;
begin
  FoundAndVisible := 0;
  EnumThreadWindows(GetCurrentThreadId, @EnumThreadWindowsProc,
    LParam(@FoundAndVisible));
  Result := FoundAndVisible > 0;
end;

////////////////////////////////////////////////////////////////////////////////
// TAutoSuggestDropdownChecker
////////////////////////////////////////////////////////////////////////////////

constructor TAutoSuggestDropdownChecker.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fSaveMessageEvent := Application.OnMessage;
  Application.OnMessage := AppOnMessage;
end;

destructor TAutoSuggestDropdownChecker.Destroy;
begin
  if (TMethod(fSaveMessageEvent).Code = TMethod(Application.OnMessage).Code)
    and (TMethod(fSaveMessageEvent).Data = TMethod(Application.OnMessage).Data)
  then begin
    Application.OnMessage := fSaveMessageEvent;
  end;
  fSaveMessageEvent := nil;
  inherited;
end;

procedure TAutoSuggestDropdownChecker.AppOnMessage(var AMsg: TMsg;
  var AHandled: Boolean);
begin
  if ((AMsg.message >= WM_KEYFIRST) and (AMsg.message <= WM_KEYLAST))
    or ((AMsg.message >= WM_MOUSEFIRST) and (AMsg.message <= WM_MOUSELAST))
    or (AMsg.message = WM_CANCELMODE)
  then
    fDroppedDown := IsAutoSuggestDropdownVisible
end;

end.

这里发布的代码只是概念验证,但可以作为那些在同样问题上挣扎的人的起点。

答案 1 :(得分:1)

我无法重现你的问题。以下OnKeyDown处理程序与KeyPreview := True一起以空的形式提供所需的行为。

procedure TMyForm.FormKeyDown(Sender: TObject; var Key: Word; 
  Shift: TShiftState);
begin
  if Key=VK_ESCAPE then
    ModalResult := mrCancel;
end;

我猜你的表单中还有其他内容正在关闭对话框。