You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi中如何将TFindDialog查找对话框的文本框改为组合框

Delphi 为TFindDialog替换ComboBox实现搜索历史功能

效果对比

  • 默认标准TFindDialog样式:
    默认查找对话框
  • 目标实现效果(带历史搜索下拉组合框):
    带组合框的查找对话框效果图

实现方案

标准TFindDialog是对系统通用查找对话框的封装,不需要从头重写整个对话框,只需要在对话框初始化时拦截窗口消息,替换原有输入框为ComboBox即可,原有全字匹配、区分大小写、查找下一个等功能可以完全保留。

核心步骤

  1. 在主窗体类中定义私有变量与方法,用于处理对话框Hook、控件替换、历史记录读写
type
  TForm1 = class(TForm)
    FindDialog1: TFindDialog;
    procedure FindDialog1Find(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    hFindCombo: HWND;
    OldComboWndProc: NativeInt;
    procedure FindDialogShow(Sender: TObject);
    procedure HookFindDialog;
    procedure CustomComboProc(var Message: TMessage);
    procedure LoadSearchHistory;
    procedure SaveSearchHistory(const Keyword: string);
  public
  end;
  1. 绑定对话框的OnShow事件,在对话框弹出时完成控件替换
procedure TForm1.FormCreate(Sender: TObject);
begin
  FindDialog1.OnShow := FindDialogShow;
end;

procedure TForm1.FindDialogShow(Sender: TObject);
begin
  HookFindDialog;
  LoadSearchHistory;
end;
  1. 实现Hook逻辑,隐藏原生Edit,在相同位置创建ComboBox
procedure TForm1.HookFindDialog;
var
  hDlg, hEdit: HWND;
  EditRect: TRect;
begin
  hDlg := FindDialog1.Handle;
  if hDlg = 0 then Exit;

  // 系统查找对话框输入框的固定控件ID为1152
  hEdit := GetDlgItem(hDlg, 1152);
  if hEdit = 0 then Exit;

  // 获取原输入框的位置、尺寸
  GetWindowRect(hEdit, EditRect);
  MapWindowPoints(HWND_DESKTOP, hDlg, EditRect, 2);
  // 隐藏原生输入框
  ShowWindow(hEdit, SW_HIDE);

  // 创建ComboBox,复用原Edit的控件ID,保证系统原有逻辑能识别输入内容
  hFindCombo := CreateWindow('COMBOBOX', nil,
    WS_CHILD or WS_VISIBLE or WS_VSCROLL or CBS_DROPDOWN or CBS_AUTOHSCROLL,
    EditRect.Left, EditRect.Top, EditRect.Right - EditRect.Left,
    200, // 下拉列表高度,可自行调整
    hDlg, 1152, HInstance, nil);

  // 匹配对话框默认字体
  SendMessage(hFindCombo, WM_SETFONT, SendMessage(hDlg, WM_GETFONT, 0, 0), 1);

  // 子类化ComboBox,兼容原生回车触发查找的交互逻辑
  OldComboWndProc := GetWindowLongPtr(hFindCombo, GWLP_WNDPROC);
  SetWindowLongPtr(hFindCombo, GWLP_WNDPROC, NativeInt(MakeObjectInstance(CustomComboProc)));
end;
  1. 实现ComboBox的自定义消息处理,同步输入内容、触发查找、保存历史
procedure TForm1.CustomComboProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_KEYDOWN:
      begin
        // 回车触发查找,和原生对话框行为一致
        if Message.WParam = VK_RETURN then
        begin
          SetLength(FindDialog1.FindText, GetWindowTextLength(hFindCombo));
          GetWindowText(hFindCombo, PChar(FindDialog1.FindText), Length(FindDialog1.FindText) + 1);
          SaveSearchHistory(FindDialog1.FindText);
          // 触发对话框默认查找按钮逻辑
          SendMessage(GetParent(hFindCombo), WM_COMMAND, MakeWParam(1, IDOK), 0);
          Exit;
        end;
      end;
    WM_DESTROY:
      begin
        // 释放子类化过程,避免内存泄漏
        if OldComboWndProc <> 0 then
        begin
          SetWindowLongPtr(hFindCombo, GWLP_WNDPROC, OldComboWndProc);
          FreeObjectInstance(Pointer(GetWindowLongPtr(hFindCombo, GWLP_WNDPROC)));
        end;
      end;
  end;
  // 其余消息走默认处理
  Message.Result := CallWindowProc(Pointer(OldComboWndProc), hFindCombo, Message.Msg, Message.WParam, Message.LParam);
end;
  1. 实现搜索历史的加载与保存逻辑
procedure TForm1.LoadSearchHistory;
var
  I: Integer;
  HistList: TStringList;
begin
  HistList := TStringList.Create;
  try
    // 实际使用时替换为自己的存储读取逻辑:ini/注册表/本地文件均可
    HistList.Delimiter := '|';
    HistList.DelimitedText := 'Delphi组件开发|TFindDialog改造|搜索历史';
    // 清空空项
    for I := HistList.Count - 1 downto 0 do
      if HistList[I].Trim = '' then HistList.Delete(I);
    // 填充到ComboBox
    SendMessage(hFindCombo, CB_RESETCONTENT, 0, 0);
    for I := 0 to HistList.Count - 1 do
      SendMessage(hFindCombo, CB_ADDSTRING, 0, LPARAM(PChar(HistList[I])));
    // 默认选中最近一次搜索词
    if HistList.Count > 0 then
      SendMessage(hFindCombo, CB_SETCURSEL, 0, 0);
  finally
    HistList.Free;
  end;
end;

procedure TForm1.SaveSearchHistory(const Keyword: string);
var
  Idx: Integer;
  HistList: TStringList;
begin
  if Keyword.Trim = '' then Exit;
  HistList := TStringList.Create;
  try
    // 实际使用时替换为自己的存储读取逻辑
    HistList.Delimiter := '|';
    HistList.DelimitedText := 'Delphi组件开发|TFindDialog改造|搜索历史';
    // 重复关键词移到最顶部
    Idx := HistList.IndexOf(Keyword);
    if Idx <> -1 then HistList.Delete(Idx);
    HistList.Insert(0, Keyword);
    // 最多保留10条历史记录,可自行调整
    while HistList.Count > 10 do HistList.Delete(HistList.Count - 1);
    // 持久化存储
    // HistList.SaveToFile(ExtractFilePath(ParamStr(0)) + 'search_hist.dat');
    // 更新ComboBox列表
    SendMessage(hFindCombo, CB_RESETCONTENT, 0, 0);
    for Idx := 0 to HistList.Count - 1 do
      SendMessage(hFindCombo, CB_ADDSTRING, 0, LPARAM(PChar(HistList[Idx])));
    SendMessage(hFindCombo, CB_SETCURSEL, 0, 0);
  finally
    HistList.Free;
  end;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
begin
  // 原有查找逻辑不需要修改,直接读取FindDialog1.FindText即可获取输入的关键词
  // 示例:Memo1.SelStart := Pos(FindDialog1.FindText, Memo1.Text) - 1;
end;

注意事项

  • 因为复用了原生输入框的控件ID,对话框自带的「查找下一个」「全字匹配」「区分大小写」等功能不需要额外适配,可正常使用。
  • 高DPI场景下,可以在创建ComboBox时根据当前对话框的DPI缩放系数调整控件尺寸,避免显示错位。
  • 对话框关闭时不需要额外手动销毁ComboBox,系统关闭对话框时会自动释放句柄。

内容的提问来源于stack exchange,提问作者lkessler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:54:33