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

Delphi ListView 欄寬變更事件攔截與處理技巧

     編輯:Delphi

攔截 ListView HDN_BEGINTRACK 訊息實現欄寬控制

Delphi 的 TListView 在報表模式下,使用者可拖曳欄標分隔線調整寬度。若需禁止調整、記錄變更或觸發自定義邏輯,需透過子類化攔截 Header 控件的 WM_NOTIFY 訊息。本文展示完整實現方案。

核心原理

  • ListView 內部包含一個 Header 控件(SysHeader32)管理欄標
  • 拖曳分隔線時,Header 發送 HDN_BEGINTRACK / HDN_ENDTRACK 通知
  • 透過 WindowProc 子類化攔截 WM_NOTIFY,檢查 HD_NOTIFY 結構

完整實現代碼

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, CommCtrl;

type
  PHD_NOTIFY = ^HD_NOTIFY;  // 別名簡化類型轉換

  TForm1 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    OldProc: TWndMethod;
    procedure NewProc(var Message: TMessage);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.NewProc(var Message: TMessage);
var
  hn: PHD_NOTIFY;
begin
  if Message.Msg = WM_NOTIFY then
  begin
    hn := PHD_NOTIFY(Message.LParam);
    // 攔截開始拖曳與結束拖曳(ANSI/Unicode 兩套代碼)
    if (hn^.hdr.code = HDN_BEGINTRACKA) or (hn^.hdr.code = HDN_BEGINTRACKW) then
    begin
      // Message.Result := 1;  // 取消註釋可「禁止調整欄寬」
      ShowMessage('偵測到開始拖曳欄寬');  // 自定義邏輯
    end
    else if (hn^.hdr.code = HDN_ENDTRACKA) or (hn^.hdr.code = HDN_ENDTRACKW) then
    begin
      ShowMessage('欄寬調整完成');  // 此處可讀取新寬度並持久化
    end
    else
      ListView1.Dispatch(Message);  // 其他訊息轉發給原處理器
  end
  else
    ListView1.Dispatch(Message);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ListView1.WindowProc := OldProc;  // 恢復原始 WindowProc,防止洩漏
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  OldProc := ListView1.WindowProc;   // 備份原始 WindowProc
  ListView1.WindowProc := NewProc;   // 安裝子類化鉤子
end;

end.

關鍵技術細節

HD_NOTIFY 結構定義


type
  HD_NOTIFY = record
    hdr: TNMHDR;      // 標準通知頭:hwndFrom, idFrom, code
    iItem: Integer;   // 欄索引
    iButton: Integer; // 滑鼠按鈕
    pitem: PHDITEM;   // 指向 HDITEM 結構(含寬度、對齊等屬性)
  end;

常見通知代碼對照

代碼觸發時機典型用途
HDN_BEGINTRACKA/W開始拖曳分隔線禁止調整、記錄初始寬度
HDN_ENDTRACKA/W結束拖曳(放開滑鼠)儲存新寬度、觸發重排版
HDN_ITEMCHANGINGA/W欄屬性即將變更驗證/修改新屬性
HDN_ITEMCHANGEDA/W欄屬性已變更同步 UI 狀態
HDN_DIVIDERDBLCLICKA/W雙擊分隔線自動調整欄寬

進階應用:持久化欄寬設定


procedure TForm1.NewProc(var Message: TMessage);
var
  hn: PHD_NOTIFY;
  i: Integer;
begin
  if (Message.Msg = WM_NOTIFY) and
     ((hn^.hdr.code = HDN_ENDTRACKA) or (hn^.hdr.code = HDN_ENDTRACKW)) then
  begin
    // 遍歷所有欄位,將寬度存入註冊表/INI
    for i := 0 to ListView1.Columns.Count - 1 do
      WriteIniInt('ListViewWidths', ListView1.Columns[i].Caption, ListView1.Columns[i].Width);
  end
  else
    ListView1.Dispatch(Message);
end;

注意事項與最佳實踐

  • 必須在 FormDestroy 恢復 WindowProc,否則表單釋放後觸發訊息會導致 AV
  • ANSI/Unicode 雙重判斷:HDN_BEGINTRACKA / HDN_BEGINTRACKW 視專案字串模式而定,建議同時檢查
  • 不要在 NewProc 中執行耗時操作,會阻塞 UI 執行緒,導致拖曳卡頓
  • 若僅需禁止調整,設定 Message.Result := 1 即可,無需 ShowMessage
  • 替代方案:TListView.OnColumnClick 無法攔截拖曳,但可用 ListView_SetColumnWidth API 事後重置

掌握這項子類化技術,不僅能控制 ListView 欄寬行為,同樣適用於 TreeView、HeaderControl 等所有發送 WM_NOTIFY 的 Windows 公共控件。

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