在TListView中绘制一个复选框 [英] drawing a checkbox in a TListView

查看:428
本文介绍了在TListView中绘制一个复选框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在 TListView 中的特定列中绘制一个复选框,因此我检查此问题如何在特定列中设置带有复选框的TListView ,并在接受的答案建议使用此另一个问题中描述的方法 如何在Delphi中设置复选框TStringGrid? ,现在移植该代码以使用ListView i:

  procedure TForm15.ListView1CustomDrawSubItem(Sender:TCustomListView; Item:TListItem; SubItem:Integer; State:TCustomDrawState; var DefaultDraw:Boolean); 
const
PADDING = 4;
var
h:HTHEME;
s:TSize;
r:TRect;
Rect:TRect;
i:Integer;
Dx:Integer;
begin
if(SubItem = 1)then
begin
DefaultDraw:= True;
Rect:= Item.DisplayRect(drBounds);
Dx:= 0;

for i:= 0 to SubItem do
Inc(Dx,Sender.Column [i] .Width);
Rect.Left:= Rect.Left + Dx;

Rect.Right:= Rect.Left + Sender.Column [SubItem + 1] .Width;

FillRect(Sender.Canvas.Handle,Rect,GetStockObject(WHITE_BRUSH));
s.cx:= GetSystemMetrics(SM_CXMENUCHECK);
s.cy:= GetSystemMetrics(SM_CYMENUCHECK);
if UseThemes then
begin
h:= OpenThemeData(Sender.Handle,'BUTTON');
if h<> 0 then
try
GetThemePartSize(h,Sender.Canvas.Handle,BP_CHECKBOX,CBS_CHECKEDNORMAL,nil,TS_DRAW,s);
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - s.cy)div 2;
r.Bottom:= r.Top + s.cy;
r.Left:= Rect.Left + PADDING;
r.Right:= r.Left + s.cx;
DrawThemeBackground(h,Sender.Canvas.Handle,BP_CHECKBOX,IfThen(CompareText(Item.SubItems [1],'True')= 0,CBS_CHECKEDNORMAL,CBS_UNCHECKEDNORMAL),r,nil);
finally
CloseThemeData(h);
end;
end
else
begin
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - s.cy)div 2;
r.Bottom:= r.Top + s.cy;
r.Left:= Rect.Left + PADDING;
r.Right:= r.Left + s.cx;
DrawFrameControl(Sender.Canvas.Handle,r,DFC_BUTTON,IfThen(CompareText(Item.SubItems [1],'True')= 0,DFCS_CHECKED,DFCS_BUTTONCHECK)
end;
// r:= Classes.Rect(r.Right + PADDING,Rect.Top,Rect.Right,Rect.Bottom);
// DrawText(Sender.Canvas.Handle,StringGrid1.Cells [ACol,ARow],length(StringGrid1.Cells [ACol,ARow]),r,DT_SINGLELINE或DT_VCENTER或DT_LEFT或DT_END_ELLIPSIS);
end
else
DefaultDraw:= False;
end;

但我失败了,我试图画一个复选框:(方向绘制列表视图中的复选框,(代码不会在列表视图中绘制任何复选框)。



列表视图是在vsReport模式,有3列,



UPDATE 1 >:由于sertac的建议设置 DefaultDraw 值现在显示复选框,但另一列看起来awfull。





UPDATE 2 ,按照Andreas建议的listview现在看起来更好,但仍然显示黑盒子;
$ b

  procedure TForm15.ListView1CustomDrawSubItem(Sender: TCustomListView;项目:TListItem; SubItem:Integer;状态:TCustomDrawState; var DefaultDraw:Boolean); 
var
h:HTHEME;
s:TSize;
r:TRect;
Rect:TRect;
i:Integer;
Dx:Integer;
begin
if(SubItem = 2)then
begin
DefaultDraw:= False;
Rect:= Item.DisplayRect(drBounds);

Dx:= 0;
for i:= 0 to SubItem-1 do
Inc(Dx,Sender.Column [i] .Width);

RectLeft:= Rect.Left + Dx;
Rect.Right:= Rect.Left + Sender.Column [SubItem] .Width;
FillRect(Sender.Canvas.Handle,Rect,GetStockObject(WHITE_BRUSH));
s.cx:= GetSystemMetrics(SM_CXMENUCHECK);
s.cy:= GetSystemMetrics(SM_CYMENUCHECK);
Dx:=(Sender.Column [SubItem] .Width-GetSystemMetrics(SM_CXMENUCHECK))div 2;
if UseThemes then
begin
h:= OpenThemeData(Sender.Handle,'BUTTON');
if h<> 0 then
try
GetThemePartSize(h,Sender.Canvas.Handle,BP_CHECKBOX,CBS_CHECKEDNORMAL,nil,TS_DRAW,s);
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - s.cy)div 2;
r.Bottom:= r.Top + s.cy;
r.Left:= Rect.Left + Dx;
r.Right:= r.Left + s.cx;
DrawThemeBackground(h,Sender.Canvas.Handle,BP_CHECKBOX,IfThen(CompareText(Item.SubItems [SubItem-1],'True')= 0,CBS_CHECKEDNORMAL,CBS_UNCHECKEDNORMAL),r,nil);
finally
CloseThemeData(h);
end;
end
else
begin
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - s.cy)div 2;
r.Bottom:= r.Top + s.cy;
r.Left:= Rect.Left + Dx;
r.Right:= r.Left + s.cx;
DrawFrameControl(Sender.Canvas.Handle,r,DFC_BUTTON,IfThen(CompareText(Item.SubItems [SubItem-1],'True')= 0,DFCS_CHECKED,DFCS_BUTTONCHECK));
end;
end;
end;


解决方案

一个比较简单的方法来摆脱这个bug以自己绘制整个项目。设置 OwnerDraw:= true ,删除 OnCustomDrawSubItem 例程,并添加

  procedure TForm15.ListView1DrawItem(Sender:TCustomListView; Item:TListItem; 
Rect:TRect;状态:TOwnerDrawState);

函数ShrinkRect(const r:TRect; const X0,X1,Y0,Y1:integer):TRect;一致;
begin
result:= r;
inc(result.Left,X0);
inc(result.Top,Y0);
dec(result.Right,X1);
dec(result.Bottom,Y1);
end;

const
CHECK_COL = 2;
PADDING = 4;
var
r:TRect;
i:Integer;
s:string;
size:TSize;
h:HTHEME;
begin

FillRect(Sender.Canvas.Handle,Rect,GetStockObject(WHITE_BRUSH));
r:= Rect;
inc(r.Left,PADDING);
for i:= 0 to TListView(Sender).Columns.Count - 1 do
begin
r.Right:= r.Left + Sender.Column [i] .Width;
if i<> CHECK_COL then
begin
如果i = 0那么
begin
s:= Item.Caption;
如果不是IsWindowVisible(ListView_GetEditControl(Sender.Handle))然后
开始
如果UseThemes和([odSelected,odHotLight] *状态<> [])然后
开始
h:= openThemeData(Sender.Handle,'LISTVIEW');
if h<> 0 then
try
DrawThemeBackground(h,Sender.Canvas.Handle,LVP_GROUPHEADER,IfThen(odSelected in State,LVGH_CLOSESELECTED,LVGH_OPENHOT),ShrinkRect(r,-2,6,1,1),nil) ;
finally
CloseThemeData(h);
end;
end;
if(odSelected in State)and not UseThemes then
DrawFocusRect(Sender.Canvas.Handle,ShrinkRect(r,-2,6,1,1));
end;
end
else
s:= Item.SubItems [i-1];
Sender.Canvas.Brush.Style:= bsClear;
DrawText(Sender.Canvas.Handle,
PChar(s),
length,s
r,
DT_SINGLELINE或DT_VCENTER或DT_LEFT或DT_END_ELLIPSIS);
end
else
begin

size.cx:= GetSystemMetrics(SM_CXMENUCHECK);
size.cy:= GetSystemMetrics(SM_CYMENUCHECK);
if UseThemes then
begin
h:= OpenThemeData(Sender.Handle,'BUTTON');
if h<> 0 then
try
GetThemePartSize(h,Sender.Canvas.Handle,BP_CHECKBOX,CBS_CHECKEDNORMAL,nil,TS_DRAW,size);
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - size.cy)div 2;
r.Bottom:= r.Top + size.cy;
r.Left:= r.Left + PADDING;
r.Right:= r.Left + size.cx;
DrawThemeBackground(h,Sender.Canvas.Handle,BP_CHECKBOX,IfThen(CompareText(Item.SubItems [1],'True')= 0,CBS_CHECKEDNORMAL,CBS_UNCHECKEDNORMAL),r,nil);
finally
CloseThemeData(h);
end;
end
else
begin
r.Top:= Rect.Top +(Rect.Bottom - Rect.Top - size.cy)div 2;
r.Bottom:= r.Top + size.cy;
r.Left:= r.Left + PADDING;
r.Right:= r.Left + size.cx;
DrawFrameControl(Sender.Canvas.Handle,r,DFC_BUTTON,IfThen(CompareText(Item.SubItems [1],'True')= 0,DFCS_CHECKED,DFCS_BUTTONCHECK));
end;

end;
inc(r.Left,Sender.Column [i] .Width);
end;

end;



上述代码需要进一步测试,但可能是正确的方向。现在已经很晚了,我得走了。


I need to draw a checkbox in a particular column in aTListView, so i check this question How can I setup TListView with CheckBoxes in only certain columns? and in the accepted answer suggest use the method described in this another question How to set a Checkbox TStringGrid in Delphi?, now porting that code to work with a ListView i come with this :

procedure TForm15.ListView1CustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
const
  PADDING = 4;
var
  h    : HTHEME;
  s    : TSize;
  r    : TRect;
  Rect : TRect;
  i    : Integer;
  Dx   : Integer;
begin
  if (SubItem=1) then
  begin
    DefaultDraw:=True;
    Rect  :=Item.DisplayRect(drBounds);
    Dx:=0;

    for i := 0 to SubItem do
    Inc(Dx,Sender.Column[i].Width);
    Rect.Left  :=Rect.Left+Dx;

    Rect.Right :=Rect.Left+Sender.Column[SubItem+1].Width;

    FillRect(Sender.Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
    s.cx := GetSystemMetrics(SM_CXMENUCHECK);
    s.cy := GetSystemMetrics(SM_CYMENUCHECK);
    if UseThemes then
    begin
      h := OpenThemeData(Sender.Handle, 'BUTTON');
      if h <> 0 then
        try
          GetThemePartSize(h, Sender.Canvas.Handle, BP_CHECKBOX, CBS_CHECKEDNORMAL, nil, TS_DRAW, s);
          r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
          r.Bottom := r.Top + s.cy;
          r.Left   := Rect.Left + PADDING;
          r.Right  := r.Left + s.cx;
          DrawThemeBackground(h, Sender.Canvas.Handle, BP_CHECKBOX, IfThen(CompareText(Item.SubItems[1],'True')=0, CBS_CHECKEDNORMAL, CBS_UNCHECKEDNORMAL), r, nil);
        finally
          CloseThemeData(h);
        end;
    end
    else
    begin
      r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
      r.Bottom := r.Top + s.cy;
      r.Left   := Rect.Left + PADDING;
      r.Right  := r.Left + s.cx;
      DrawFrameControl(Sender.Canvas.Handle, r, DFC_BUTTON, IfThen(CompareText(Item.SubItems[1],'True')=0, DFCS_CHECKED, DFCS_BUTTONCHECK));
    end;
   //r := Classes.Rect(r.Right + PADDING, Rect.Top, Rect.Right, Rect.Bottom);
   // DrawText(Sender.Canvas.Handle,   StringGrid1.Cells[ACol, ARow], length(StringGrid1.Cells[ACol, ARow]),  r, DT_SINGLELINE or DT_VCENTER or DT_LEFT or DT_END_ELLIPSIS);
  end
  else
  DefaultDraw:=False;
end;

but i fail miserably in my attempt to draw a checkbox :(, can someone point me in the right direction to draw the checkbox in the listview, (the code does not draw any checkbox in the listview).

The listview is in vsReport mode and had 3 columns, i want put the checkbox in the third column. please don't suggest which use a thrid party component, i want use the TlistView control.

UPDATE 1 : thanks to the sertac recomendattion setting the DefaultDraw value now the checkboxes are shown, but the another columns looks awfull.

UPDATE 2 , following the Andreas suggestions the listview now look better, but still shown the black box;

procedure TForm15.ListView1CustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  h    : HTHEME;
  s    : TSize;
  r    : TRect;
  Rect : TRect;
  i    : Integer;
  Dx   : Integer;
begin
  if (SubItem=2) then
  begin
    DefaultDraw:=False;
    Rect  :=Item.DisplayRect(drBounds);

    Dx:=0;
    for i := 0 to SubItem-1 do
      Inc(Dx,Sender.Column[i].Width);

    Rect.Left  :=Rect.Left+Dx;
    Rect.Right :=Rect.Left+Sender.Column[SubItem].Width;
    FillRect(Sender.Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
    s.cx := GetSystemMetrics(SM_CXMENUCHECK);
    s.cy := GetSystemMetrics(SM_CYMENUCHECK);
    Dx   := (Sender.Column[SubItem].Width-GetSystemMetrics(SM_CXMENUCHECK)) div 2;
    if UseThemes then
    begin
      h := OpenThemeData(Sender.Handle, 'BUTTON');
      if h <> 0 then
        try
          GetThemePartSize(h, Sender.Canvas.Handle, BP_CHECKBOX, CBS_CHECKEDNORMAL, nil, TS_DRAW, s);
          r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
          r.Bottom := r.Top + s.cy;
          r.Left   := Rect.Left + Dx;
          r.Right  := r.Left + s.cx;
          DrawThemeBackground(h, Sender.Canvas.Handle, BP_CHECKBOX, IfThen(CompareText(Item.SubItems[SubItem-1],'True')=0, CBS_CHECKEDNORMAL, CBS_UNCHECKEDNORMAL), r, nil);
        finally
          CloseThemeData(h);
        end;
    end
    else
    begin
      r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
      r.Bottom := r.Top + s.cy;
      r.Left   := Rect.Left + Dx;
      r.Right  := r.Left + s.cx;
      DrawFrameControl(Sender.Canvas.Handle, r, DFC_BUTTON, IfThen(CompareText(Item.SubItems[SubItem-1],'True')=0, DFCS_CHECKED, DFCS_BUTTONCHECK));
    end;
  end;
end;

解决方案

One relatively simple way to get rid of this bug is to owner-draw the entire item. Set OwnerDraw := true, remove your OnCustomDrawSubItem routine, and add

procedure TForm15.ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
  Rect: TRect; State: TOwnerDrawState);

  function ShrinkRect(const r: TRect; const X0, X1, Y0, Y1: integer): TRect; inline;
  begin
    result := r;
    inc(result.Left, X0);
    inc(result.Top, Y0);
    dec(result.Right, X1);
    dec(result.Bottom, Y1);
  end;

const
  CHECK_COL = 2;
  PADDING = 4;
var
  r: TRect;
  i: Integer;
  s: string;
  size: TSize;
  h: HTHEME;
begin

  FillRect(Sender.Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
  r := Rect;
  inc(r.Left, PADDING);
  for i := 0 to TListView(Sender).Columns.Count - 1 do
  begin
    r.Right := r.Left + Sender.Column[i].Width;
    if i <> CHECK_COL then
    begin
      if i = 0 then
      begin
        s := Item.Caption;
        if not IsWindowVisible(ListView_GetEditControl(Sender.Handle)) then
        begin
          if UseThemes and ([odSelected, odHotLight] * State <> []) then
          begin
            h := OpenThemeData(Sender.Handle, 'LISTVIEW');
            if h <> 0 then
              try
                DrawThemeBackground(h, Sender.Canvas.Handle, LVP_GROUPHEADER, IfThen(odSelected in State, LVGH_CLOSESELECTED, LVGH_OPENHOT), ShrinkRect(r, -2, 6, 1, 1), nil);
              finally
                CloseThemeData(h);
              end;
          end;
          if (odSelected in State) and not UseThemes then
            DrawFocusRect(Sender.Canvas.Handle, ShrinkRect(r, -2, 6, 1, 1));
        end;
      end
      else
        s := Item.SubItems[i - 1];
      Sender.Canvas.Brush.Style := bsClear;
      DrawText(Sender.Canvas.Handle,
        PChar(s),
        length(s),
        r,
        DT_SINGLELINE or DT_VCENTER or DT_LEFT or DT_END_ELLIPSIS);
    end
    else
    begin

      size.cx := GetSystemMetrics(SM_CXMENUCHECK);
      size.cy := GetSystemMetrics(SM_CYMENUCHECK);
      if UseThemes then
      begin
        h := OpenThemeData(Sender.Handle, 'BUTTON');
        if h <> 0 then
          try
            GetThemePartSize(h, Sender.Canvas.Handle, BP_CHECKBOX, CBS_CHECKEDNORMAL, nil, TS_DRAW, size);
            r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - size.cy) div 2;
            r.Bottom := r.Top + size.cy;
            r.Left   := r.Left + PADDING;
            r.Right  := r.Left + size.cx;
            DrawThemeBackground(h, Sender.Canvas.Handle, BP_CHECKBOX, IfThen(CompareText(Item.SubItems[1],'True')=0, CBS_CHECKEDNORMAL, CBS_UNCHECKEDNORMAL), r, nil);
          finally
            CloseThemeData(h);
          end;
      end
      else
      begin
        r.Top    := Rect.Top + (Rect.Bottom - Rect.Top - size.cy) div 2;
        r.Bottom := r.Top + size.cy;
        r.Left   := r.Left + PADDING;
        r.Right  := r.Left + size.cx;
        DrawFrameControl(Sender.Canvas.Handle, r, DFC_BUTTON, IfThen(CompareText(Item.SubItems[1],'True')=0, DFCS_CHECKED, DFCS_BUTTONCHECK));
      end;

    end;
    inc(r.Left, Sender.Column[i].Width);
  end;

end;

The code above needs further testing, but is probably in the right direction. Now it's very late, and I have to go.

这篇关于在TListView中绘制一个复选框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆