迭代窗体上的控件时,如何识别特定按钮?

时间:2016-09-01 16:29:35

标签: delphi winapi

在向用户显示之前,我需要对TaskDialog进行一些更改。使用Windows API调用来处理对话框中的每个控件都相当简单。我需要更加确定我找到了哪个按钮。我希望找到一个地方,我可以读取按钮在按下时给出的结果。

换句话说,如果我按下一个会导致返回值的按钮(在Delphi中,它被称为模态结果)为100,我本来期望有一个API调用我可以调用以找出按钮的内容“回报价值”将是。我还没有找到这样的电话。

我不想依赖按钮文字..

这是我到目前为止所拥有的。

function EnumWindowsProcToFindDlgControls(hWindow: HWND; _param:LPARAM): BOOL; stdcall;
var
  sClassName:string;
  hBMP:THandle;
  i:integer;
begin
  SetLength(sClassName, MAX_PATH);
  GetClassName(hWindow, PChar(sClassName), MAX_PATH);
  SetLength(sClassName, StrLen(PChar(sClassName)));

  if sClassName='Button' then
    begin    
      // always 0...
      i:=GetDlgCtrlID(hWindow);

      if (i=100) or (i=102) then
        begin
          hBmp := LoadImage(HInstance, 'DISA', IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE or LR_LOADTRANSPARENT );

          SendMessage(hWindow, BM_SETIMAGE, WPARAM(IMAGE_BITMAP), LPARAM(hBmp));
        end;
    end;

  // keep looking
  Result:=true;
end;

procedure TForm2.TaskDialog1DialogConstructed(Sender: TObject);
begin
  EnumChildWindows(TaskDialog1.Handle, @EnumWindowsProcToFindDlgControls, 0);
end;

我怀疑用对话框做这样的事情并不完全“可敬”。

这是一个使用Delphi的VCL TTaskDialog组件的Delphi 10 Win32应用程序,它是Windows任务对话框功能的包装器。在显示之前,OnConstructed事件将触发,执行此代码。

感谢您的帮助!

1 个答案:

答案 0 :(得分:10)

Win32按钮没有“返回值”,这就是没有API从中检索这样的值的原因。你在想什么是严格的VCL功能。

在Win32 API术语中,按钮可以具有控件ID,例如,在MessageBox()的情况下,会将IDOKIDCANCEL等标准ID值分配给对话框按钮。单击一个按钮并关闭对话框时,按钮的控件ID将用作函数返回值。

但是任务对话框不使用控件ID,这就是为什么你没有看到任何分配给对话框按钮的原因。

要识别特定的任务对话框按钮,我可以想到两种方法:

    在子枚举期间
  1. 检索每个按钮的标题文本(GetWindowText()),并将其与您感兴趣的标题进行比较。只需知道标准按钮(来自TTaskDialog.CommonButtons属性)即可使用本地化文本,除非您可以控制应用程序的区域设置,否则不会使其成为定位标准按钮的合适选项。

  2. 向对话框发送TDM_ENABLE_BUTTON消息以暂时禁用具有给定ID的所需按钮,然后枚举对话框的控件,直到找到禁用的子窗口(使用IsWindowEnabled()),并且然后重新启用控件。然后,您可以根据需要操作找到的窗口。

    对于操作按钮的Task Dialog messagesTask Dialog Notifications(例如触发TTaskDialog.OnButtonClicked事件的TDN_BUTTON_CLICKED),标准按钮使用IDOK等ID, IDCANCEL等自定义按钮(来自TTaskDialog.Buttons属性)使用其ModalResult属性作为其ID。

    您可以通过TDM_ENABLE_BUTTON直接发送SendMessage()标准按钮,也可以通过TTaskDialogBaseButtonItem.Enabled属性发送自定义按钮。

  3. 对于#2,这在我尝试时起作用:

    uses
      Winapi.CommCtrl;
    
    function FindDisabledDlgControl(hWindow: HWND; _param: LPARAM): BOOL; stdcall;
    type
      PHWND = ^HWND;
    begin
      if not IsWindowEnabled(hWindow) then
      begin
        PHWND(_param)^ := hWindow;
        Result := False;
      end else
        Result := True;
    end;
    
    procedure TForm2.TaskDialog1DialogConstructed(Sender: TObject);
    var
      hButton: HWND;
    begin
      // common tcbOk button
      SendMessage(TaskDialog1.Handle, TDM_ENABLE_BUTTON, IDOK, 0);
      hButton := 0;
      EnumChildWindows(TaskDialog1.Handle, @FindDisabledDlgControl, LPARAM(@hButton));
      SendMessage(TaskDialog1.Handle, TDM_ENABLE_BUTTON, IDOK, 1);
      if hButton <> 0 then
      begin
        // use hButton as needed...
      end;
    
      // custom button
      TaskDialog1.Buttons[0].Enabled := False;
      hButton := 0;
      EnumChildWindows(TaskDialog1.Handle, @FindDisabledDlgControl, LPARAM(@hButton));
      TaskDialog1.Buttons[0].Enabled := True;
      if hButton <> 0 then
      begin
        // use hButton as needed...
      end;
    end;