SendMessage(hWnd, Msg, wParam, lParam) 直接將消息發送到窗口過程,繞過消息隊列,同步阻塞直到目標窗口處理完返回。適用於即時生效的屬性設置、模擬用戶操作、跨進程控件控制。
問題: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 可動態修改窗口風格位。
難點:滾動條消息由系統直接 SendMessage 發送給 ListBox,不經過主窗體消息隊列,Application.OnMessage、HookMainWindow 均捕獲不到。必須子類化 (重寫 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;
procedure TForm1.ListBoxMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
FMousePos.X := X; FMousePos.Y := Y; // FMousePos: TPoint 字段
end;
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_SETDROPPEDWIDTH | wParam=像素寬度,lParam=0。需在 Items 填充後調用。 |
| 按鈕風格修改 | BM_SETSTYLE | wParam=新風格 (BS_RADIOBUTTON 等),lParam=1 (重繪)。 |
| ListBox 滾動結束 | WM_VSCROLL + SB_ENDSCROLL | 必須子類化 WndProc 攔截。配合 ItemAtPos 需實時鼠標座標。 |
SendMessage。WndProc、調用 inherited 是標準模式。避免使用 SetWindowLong(GWL_WNDPROC) 直接掛鉤,易導致崩潰且難調試。CB_SETDROPPEDWIDTH、SB_ENDSCROLL 等定義在 CommCtrl.pas/Windows.pas,確保 uses 引入。wParam/lParam 在 64 位為 8 字節,TMessage 已適配,直接使用 Msg.WParam/Msg.LParam 或 WParamLo/WParamHi 輔助函數。SendMessage 是 Delphi 突破 VCL 封裝限制、直達 Windows 內核控件行為的核心工具。掌握「動態屬性設置」與「消息子類化攔截」兩大模式,可解決下拉寬度自適應、滾動位置追蹤、外觀動態變更等疑難需求,構建更專業的用戶界面。