본문 바로가기

프로그래밍/Delphi

How to draw a checkbox to column in a TListView

How to draw a checkbox to column in a TListView

델파이(Delphi)에서 TListView의 특정 컬럼에 체크박스 표시하기

 

TStringGrid 또는 TListView의 특정 컬럼에만 체크박스를 표시하는 방법을 알아보자

use 절에는 다음과 같은 유닛을 포함시켜야 한다

 

..
uses 
  // ListView_GetEditControl : ListView의 Items의 Text Edit를 위한 핸들을 얻는함수 
  commctrl, 
  // 테마(Theme) 사용시 테마정보를 얻기 위한 
  UxTheme, 
  // IfThen 함수 사용을 위한 
  Math, 
 .. 

 

 

TListView의 OwnerDraw Property = true로 지정후 "OnDrawItem"을 다음과 같이 작성한다.  2번째 SubItem 컬럼에 CheckBox를 표시 하도록 하는 예제

 

procedure TForm1.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; //2번째 SubItems 컬럼에 체크박스를 표시
  PADDING = 4;
var
  r: TRect;
  i: Integer;
  s: string;
  size: TSize;
  h: HTHEME;
begin
  //RowSelect=true인 경우 노드의 선택에 따른 배경색 지정
  if Item.Selected then
    FillRect(Sender.Canvas.Handle, Rect, GetStockObject(LTGRAY_BRUSH))
  else
    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;
    // 체크박스 표시 대상 컬럼이 아닌 경우는 기본 Drawing
    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 // UseThemes는 프로젝트에 테마 사용시 true
      begin
        //테마가 사용된 경우는 해당 테마의 UX정보를 가져와야 한다.
        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 //테마가 사용되지 않은 경우는 기본 컨트롤 UX 사용
      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;
        // DFCS_CHECKED, DFCS_BUTTONCHECK : 체크박스의 체크, 비체크 상태를 판별 한다.
        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;

 

처리결과