This is related to the question How to show a check box in TListView header column?.
I want to use the code from this answer by @Sertac Akyuz
. (I need this to work in WinXP also)
But I want to make the header CheckBox to not steal focus from the ListView or other active controls.
A fast workaround is to set the focus always to the ListView in the ListHeaderWndProc
:
...
FListHeaderChk.Checked := not FListHeaderChk.Checked;
ListView1.SetFocus;
// code that checks/clears all items
But this is kinda ugly. Because the CheckBox is first focused and then the focus goes back to the ListView. also If I click the CheckBox and drag the mouse outside the CheckBox it fails to receive the BN_CLICKED
message.
I have also tried:
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;
Nothing works. What am I missing?
Do not process the WM_COMMAND
message for a BN_CLICKED
notification, the behavior you want about having a click while pressing the button and then draggging outside after the click won't work otherwise. A click consists of pressing and releasing the button on the control.
Instead you can look for if the mouse is clicked inside the control and then switch the checked state if it is. You can eat the mouse message after that, so the control won't receive the focus. But this should be checked in the window procedure of the checkbox, not the list view. Modified code:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With