这与问题
How to show a check box in TListView header column?有关.
我想使用@Sertac Akyuz的this answer代码. (我还需要这个在WinXP中工作)
但我想让标题CheckBox不从ListView或其他活动控件中窃取焦点.
一个快速的解决方法是将焦点始终设置为ListHeaderWndProc中的ListView:
... FListHeaderChk.Checked := not FListHeaderChk.Checked; ListView1.SetFocus; // code that checks/clears all items
但这有点难看.因为CheckBox首先被聚焦,然后焦点返回到ListView.如果我单击CheckBox并将鼠标拖到CheckBox外部,则无法收到BN_CLICKED消息.
我也尝试过:
TCheckBox = class(StdCtrls.TCheckBox) private procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE; protected procedure CreateParams(var Params: TCreateParams); override; public procedure DefaultHandler(var Message); override; end; procedure TCheckBox.WMMouseActivate(var Message: TWMMouseActivate); begin Message.Result := MA_NOACTIVATE; // no effect! end; procedure TCheckBox.CreateParams(var Params: TCreateParams); const WS_EX_NOACTIVATE = $08000000; begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_NOACTIVATE; // no effect! end; procedure TCheckBox.DefaultHandler(var Message); begin case TMessage(Message).Msg of WM_SETFOCUS: begin if IsWindow(TWMSetFocus(Message).FocusedWnd) then begin TMessage(Message).Result := 1; // ??? // inherited // ??? Windows.SetFocus(TWMSetFocus(Message).FocusedWnd); Exit; // CheckBox fails to receive `BN_CLICKED` message end; end; end; inherited; end;
什么都行不通.我错过了什么?
解决方法
不要为BN_CLICKED通知处理WM_COMMAND消息,在按下按钮时想要点击一下然后在点击之后向外拖动的行为将不起作用.单击包括按下并释放控件上的按钮.
相反,您可以查找是否在控件内单击鼠标,然后切换检查状态(如果是).之后您可以吃掉鼠标消息,因此控件将无法获得焦点.但是应该在复选框的窗口过程中检查,而不是列表视图.修改后的代码
unit Unit1; interface uses Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,ComCtrls,StdCtrls; type TForm1 = class(TForm) ListView1: TListView; CheckBox1: TCheckBox; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FListHeaderChk: TCheckBox; FSaveListHeaderChkWndProc: TWndMethod; FListHeaderWnd: HWND; procedure ListHeaderChkWndProc(var Msg: TMessage); end; var Form1: TForm1; implementation uses commctrl; {$R *.dfm} function GetCheckSize: TPoint; // from checklst.pas begin with TBitmap.Create do try Handle := LoadBitmap(0,PChar(OBM_CHECKBoxES)); Result.X := Width div 4; Result.Y := Height div 3; finally Free; end; end; procedure TForm1.FormCreate(Sender: TObject); var CheckSize: TPoint; HeaderSize: TRect; begin ListView1.HandleNeeded; FListHeaderWnd := ListView_GetHeader(ListView1.Handle); FListHeaderChk := TCheckBox.Create(nil); CheckSize := GetCheckSize; FListHeaderChk.Height := CheckSize.X; FListHeaderChk.Width := CheckSize.Y; ShowWindow(ListView1.Handle,SW_SHOWNORMAL); windows.GetClientRect(FListHeaderWnd,HeaderSize); FListHeaderChk.Top := (HeaderSize.Bottom - FListHeaderChk.Height) div 2; FListHeaderChk.Left := FListHeaderChk.Top; FListHeaderChk.Parent := Self; FListHeaderChk.TabStop := False; windows.SetParent(FListHeaderChk.Handle,FListHeaderWnd); FSaveListHeaderChkWndProc := FListHeaderChk.WindowProc; FListHeaderChk.WindowProc := ListHeaderChkWndProc; end; procedure TForm1.FormDestroy(Sender: TObject); begin FListHeaderChk.Free; end; procedure TForm1.ListHeaderChkWndProc(var Msg: TMessage); begin if (Msg.Msg = WM_MOUSEACTIVATE) and (Msg.LParamLo = HTCLIENT) then begin Msg.Result := MA_NOACTIVATEANDEAT; FListHeaderChk.Checked := not FListHeaderChk.Checked; Exit; end; FSaveListHeaderChkWndProc(Msg); end; end.