首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi SendMessage 進階技巧:動態設置 ComboBox 下拉寬度與攔截 ListBox 滾動消息

     編輯:Delphi

SendMessage 基礎

SendMessage(hWnd, Msg, wParam, lParam) 直接將消息發送到窗口過程,繞過消息隊列,同步阻塞直到目標窗口處理完返回。適用於即時生效的屬性設置、模擬用戶操作、跨進程控件控制。

技巧一:動態設置 ComboBox 下拉列表寬度

問題:ComboBox.Width 固定,下拉項文本過長被截斷。解決:計算最寬項目像素寬,發送 CB_SETDROPPEDWIDTH。

procedure TForm1.AdjustComboDropWidth(Combo: TComboBox; Query: TQuery; FieldName: string);
var MaxWidth, w: Integer; i: Integer;
begin
  MaxWidth := 0;
  Query.Open;
  try
    while not Query.Eof do
    begin
      w := Combo.Canvas.TextWidth(Query.FieldByName(FieldName).AsString);
      if w > MaxWidth then MaxWidth := w;
      Query.Next;
    end;
  finally Query.Close; end;
  // 發送消息,立即生效
  SendMessage(Combo.Handle, CB_SETDROPPEDWIDTH, MaxWidth + 20, 0); // +20 預留滾動條/邊距
end;

優勢:無需重建控件、無閃爍、運行時動態適配數據長度。

技巧二:模擬按鈕風格切換 (趣味示例)

SendMessage(Button1.Handle, BM_SETSTYLE, BS_RADIOBUTTON, 1); // 點擊後變單選按鈕外觀
SendMessage(Button1.Handle, BM_SETSTYLE, BS_CHECKBOX, 1);     // 變複選框外觀

演示 SendMessage 可動態修改窗口風格位。

技巧三:攔截 ListBox 垂直滾動消息 (WM_VSCROLL)

難點:滾動條消息由系統直接 SendMessage 發送給 ListBox,不經過主窗體消息隊列,Application.OnMessage、HookMainWindow 均捕獲不到。必須子類化 (重寫 WndProc)。

步驟 1:繼承 TListBox 並重寫 WndProc

type
  TMyListBox = class(TListBox)
  private
    procedure WndProc(var Msg: TMessage); override;
  public
  end;

procedure TMyListBox.WndProc(var Msg: TMessage);
begin
  if (Msg.Msg = WM_VSCROLL) and (Msg.WParamLo = SB_ENDSCROLL) then
  begin
    // 滾動結束,獲取鼠標位置對應項索引
    // 需配合 OnMouseMove 記錄當前鼠標座標
    if Assigned(Form1) then
      Form1.Edit1.Text := IntToStr(ItemAtPos(Form1.FMousePos, True));
    inherited; // 必須調用繼承,否則滾動條不繪製/不響應
  end
  else
    inherited;
end;

步驟 2:主窗體記錄鼠標位置

procedure TForm1.ListBoxMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  FMousePos.X := X; FMousePos.Y := Y; // FMousePos: TPoint 字段
end;

步驟 3:運行時創建並綁定事件

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
  FMousePos := Point(0,0);
  FList := TMyListBox.Create(Self);
  with FList do
  begin
    Parent := Self; Left := 5; Top := 30; Width := 150; Height := 200;
    for i := 0 to 300 do Items.Add(IntToStr(i));
    OnMouseMove := ListBoxMouseMove; // 綁定記錄鼠標位置
  end;
end;

關鍵點總結

場景消息/常量關鍵點
ComboBox 下拉寬度CB_SETDROPPEDWIDTHwParam=像素寬度,lParam=0。需在 Items 填充後調用。
按鈕風格修改BM_SETSTYLEwParam=新風格 (BS_RADIOBUTTON 等),lParam=1 (重繪)。
ListBox 滾動結束WM_VSCROLL + SB_ENDSCROLL必須子類化 WndProc 攔截。配合 ItemAtPos 需實時鼠標座標。

最佳實踐建議

  1. 優先用 VCL 封裝屬性/方法:只有 VCL 未暴露的底層行為 (如下拉寬度、滾動通知) 才用 SendMessage。
  2. 子類化慎用:繼承控件、重寫 WndProc、調用 inherited 是標準模式。避免使用 SetWindowLong(GWL_WNDPROC) 直接掛鉤,易導致崩潰且難調試。
  3. 常量聲明:CB_SETDROPPEDWIDTH、SB_ENDSCROLL 等定義在 CommCtrl.pas/Windows.pas,確保 uses 引入。
  4. 64 位兼容:wParam/lParam 在 64 位為 8 字節,TMessage 已適配,直接使用 Msg.WParam/Msg.LParam 或 WParamLo/WParamHi 輔助函數。

總結

SendMessage 是 Delphi 突破 VCL 封裝限制、直達 Windows 內核控件行為的核心工具。掌握「動態屬性設置」與「消息子類化攔截」兩大模式,可解決下拉寬度自適應、滾動位置追蹤、外觀動態變更等疑難需求,構建更專業的用戶界面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved