【问题标题】:How to show a check box in TListView header column?如何在 ListView 标题列中显示复选框?
【发布时间】:2012-01-04 16:26:56
【问题描述】:

我需要在 TListView 的列标题中有一个复选框:

我已经尝试了以下代码:

with CheckBox1 do
begin
  Parent := ListView1;
  Top := 0;
  Left := 4;
end;

但复选框并不总是按预期工作。如何在 TListView 标题列中正确创建复选框?

【问题讨论】:

  • @AndreasRejbrand 我不认为该问题涉及在 header 列中绘制复选框?
  • “CheckBox 有点小错误”是什么意思?
  • 更接近的是使用Parent:=ListView1;这个Windows.SetParent(Handle, ListView_GetHeader(ListView1.Handle));这一行,但随后复选框停止响应点击。
  • @dreei,这里的任何答案对您有帮助吗?请给我们一些反馈好吗?你能详细说明little buggy 是什么意思吗?

标签: delphi listview tcheckbox


【解决方案1】:

以下代码会将复选框添加到列表视图的标题中,并显示如何为其处理单击事件。

请注意,从 Windows Vista 开始支持以下代码。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
  private
    HeaderID: Integer;
    procedure WMNotify(var AMessage: TWMNotify); message WM_NOTIFY;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WMNotify(var AMessage: TWMNotify);
begin
  if AMessage.NMHdr^.idFrom = HeaderID then
    if AMessage.NMHdr^.code = HDN_ITEMSTATEICONCLICK then
      ShowMessage('You have clicked the header check box');

  inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  HeaderHandle: HWND;
  HeaderItem: HD_ITEM;
  HeaderStyle: Integer;
begin
  ListView_SetExtendedListViewStyle(ListView1.Handle, LVS_EX_CHECKBOXES or LVS_EX_FULLROWSELECT);
  HeaderHandle := ListView_GetHeader(ListView1.Handle);
  HeaderStyle := GetWindowLong(HeaderHandle, GWL_STYLE);
  HeaderStyle := HeaderStyle or HDS_CHECKBOXES;
  SetWindowLong(HeaderHandle, GWL_STYLE, HeaderStyle);

  HeaderItem.Mask := HDI_FORMAT;
  Header_GetItem(HeaderHandle, 0, HeaderItem);
  HeaderItem.fmt := HeaderItem.fmt or HDF_CHECKBOX or HDF_FIXEDWIDTH;
  Header_SetItem(HeaderHandle, 0, HeaderItem);

  HeaderID := GetDlgCtrlID(HeaderHandle);
end;

end.


【讨论】:

  • @David,谢谢。正如他们所说的here HDS_CHECKBOXES 标头样式可从ComCtl32.dll 版本 6.00 获得,might be 也适用于 Windows XP。但我不明白为什么在没有适当通知的情况下这种标题样式会存在于 Windows XP 中。
  • 不,你是对的。我又说废话了。对不起!这就是包含 XP 清单时得到的结果。我会删除我的烂摊子。
  • 但是,无论如何,我很遗憾地说您的程序没有在 XP 的标题中显示复选框。我相信 Vista 和 comctl32 v6 的意思是您必须同时拥有 Vista 和 comctl v6 清单。换句话说,如果您在 vista 上运行但没有显示 comctl v6,那么您不会获得标题复选框。所以这只是我害怕的 Vista。
  • 有没有让行复选框只读?我已经设置了这样的列表视图,但仍然可以切换复选框。
  • @ikathegreat,我找不到 any style 和例如ListView_SetCheckState 宏只允许设置或重置复选框选中状态。所以我认为没有办法禁用这些复选框。
【解决方案2】:

如果您的目标是 Vista 及更高版本,显而易见的答案是 TLama's。如果没有,请将标题控件中的复选框设置为父项,而不是列表框中的父项(同样 TLama 对问题发表了评论)。该复选框将向其父级(标头控件)发送通知,因此您需要对其进行子类化。工作样本:

type
  TForm1 = class(TForm)
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FListHeaderWnd: HWND;
    FListHeaderChk: TCheckBox;
    FSaveListHeaderWndProc, FListHeaderWndProc: Pointer;
    procedure ListHeaderWndProc(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;

  // the below won't show anything since the form is not visible yet
  ShowWindow(ListView1.Handle, SW_SHOWNORMAL); // otherwise header is not sized
  windows.GetClientRect(FListHeaderWnd, HeaderSize);
  FListHeaderChk.Top := (HeaderSize.Bottom - FListHeaderChk.Height) div 2;
  FListHeaderChk.Left := FListHeaderChk.Top;

  FListHeaderChk.Parent := Self;
  windows.SetParent(FListHeaderChk.Handle, FListHeaderWnd);

  FListHeaderWndProc := classes.MakeObjectInstance(ListHeaderWndProc);
  FSaveListHeaderWndProc := Pointer(GetWindowLong(FListHeaderWnd, GWL_WNDPROC));
  SetWindowLong(FListHeaderWnd, GWL_WNDPROC, NativeInt(FListHeaderWndProc));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetWindowLong(FListHeaderWnd, GWL_WNDPROC, NativeInt(FSaveListHeaderWndProc));
  classes.FreeObjectInstance(FListHeaderWndProc);
  FListHeaderChk.Free;
end;

procedure TForm1.ListHeaderWndProc(var Msg: TMessage);
begin
  if (Msg.Msg = WM_COMMAND) and (HWND(Msg.LParam) = FListHeaderChk.Handle)
        and (Msg.WParamHi = BN_CLICKED) then begin
    FListHeaderChk.Checked := not FListHeaderChk.Checked;

    // code that checks/clears all items

  end;

  Msg.Result := CallWindowProc(FSaveListHeaderWndProc, FListHeaderWnd,
                               Msg.Msg, Msg.WParam, Msg.LParam);
end;

请注意,如果您设置了“ColumnClick”,那么当您单击复选框时,该复选框不会与标题按钮一起“推送”,这看起来很难看。

【讨论】:

  • +1 我正在采用这个 :) 但我 breaking my head 关于如何使复选框不再从 ListView 或其他活动控件中获得焦点...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 2014-02-21
  • 2019-03-06
  • 1970-01-01
  • 2019-05-24
相关资源
最近更新 更多