Question

Following on from this question and its very useful answer: I implemented a variation of Ken's answer in my big project where I had similar code appearing in many forms. But I noticed that I also had some forms with DrawColumnCell handlers as follows

procedure TEditDocket.DBGrid1DrawColumnCell(Sender: TObject;
           const Rect: TRect; DataCol: Integer; Column: TColumn;
           State: TGridDrawState);
var
 DrawRect: TRect;

begin
 if column.Index  = 3 then
  begin
   DrawRect:= Rect;
   drawrect.left:= rect.left + 24;
   InflateRect (DrawRect, -1, -1);
   dbgrid1.Canvas.FillRect (Rect);
   DrawFrameControl (dbgrid1.Canvas.Handle, DrawRect, DFC_BUTTON,
                 ISChecked[Column.Field.AsInteger]);
  end
 else dbgrid1.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;

How I could combine the above code with a general OnDrawColumnCell handler? Is it possible to define the general handler with an extra parameter (which would be the column index; if it were -1 then the above code would not execute)? How would I pass such a parameter to the handler?

Was it helpful?

Solution 2

Independently, I reached the following answer using the 'tag' property of the grid.

In the client form, I write

 dbGrid1.OnDrawColumnCell:= dm.DBGrid1DrawColumnCell;
 dbGrid1.tag:= 3;  // column with checkbox
 dbGrid2.OnDrawColumnCell:= dm.DBGrid1DrawColumnCell;
 dbGrid2.tag:= $23;  // both columns 2 and 3 are checkboxes 

The default handler is now

procedure TDm.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
          DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
  IsChecked : array[0..1] of Integer =
                (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);

var
 DrawRect: TRect;
 tag: integer;
 cols: set of byte;

begin
 if sender is TDBGrid then
  begin
   tag:= TDBGrid (sender).tag;
   if (THackDBGrid (sender).DataLink.ActiveRecord + 1 = THackDBGrid (sender).Row)
   or (gdFocused in State) or (gdSelected in State) then
    with TDBGrid (sender) do
     begin
      canvas.Brush.Color:= clMoneyGreen;
      canvas.font.color:= clNavy;
     end;

   cols:= [];
   while tag > 0 do
    begin
     cols:= cols + [tag mod 16];
     tag:= tag div 16
    end;

   if column.Index in cols then
    begin
     DrawRect:= Rect;
     drawrect.left:= rect.left + 24;
     InflateRect (DrawRect, -1, -1);
     TDBGrid (sender).Canvas.FillRect (Rect);
     DrawFrameControl (TDBGrid (sender).Canvas.Handle, DrawRect, DFC_BUTTON,
                   ISChecked[Column.Field.AsInteger]);
    end
   else
    begin
     TDBGrid (sender).DefaultDrawColumnCell (Rect, DataCol, Column, State);
    end;
  end;
end;

This means that column 0 must never be a checkbox. Using a set allows up to four columns in the grid to be checkboxes.

OTHER TIPS

I see (from the comment chain) that you're wanting to call either the generic or (in certain cases) also call the code for the checkbox drawing.

the generic OnDrawColumnCell handler handles the background colour for the cell. How can I extend this to create a handler which will also draw checkboxes when they're needed? Incidentally, the generic code has to be called before the checkbox code, not after.

That's actually quite simple. Define both methods with the proper signature (the generic one, and the checkbox one) (code untested!). Only connect the generic event to the TDBGrid.OnDrawColumnCell events, though - the checkbox one will be chained in if needed:

// Generic (from my other post) - notice method name has changed
procedure TDataModule1.GenericDrawColumnCell(Sender: TObject; 
  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
  RowColors: array[Boolean] of TColor = (clSilver, clDkGray);
var
  OddRow: Boolean;
  Grid: TDBGrid;
begin
  if (Sender is TDBGrid) then
  begin
    Grid := TDBGrid(Sender);
    OddRow := Odd(Grid.DataSource.DataSet.RecNo);
    Grid.Canvas.Brush.Color := RowColors[OddRow];
    Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);

    // If you want the check box code to only run for a single grid,
    // you can add that check here using something like
    //
    // if (Column.Index = 3) and (Sender = DBGrid1) then
    //
    if (Column.Index = 3) then // 
      CheckBoxDrawColumCell(Sender, Rect, DataCol, Column, State)
  end;
end;

// Checkbox (from yours) - again, notice method name change.
procedure TEditDocket.CheckBoxDrawColumnCell(Sender: TObject; 
  const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
 DrawRect: TRect;
 Grid: TDBGrid;
begin
  // Don't use DBGrid1, because it makes the code specific to a single grid.
  // If you need it for that, make sure this code only gets called for that 
  // grid instead in the generic handler; you can then use it for another 
  // grid later (or a different one) without breaking any code
  if column.Index = 3 then
  begin
    Grid := TDBGrid(Sender);
    DrawRect:= Rect;
    Drawrect.Left := Rect.Left + 24;
    InflateRect (DrawRect, -1, -1);
    Grid.Canvas.FillRect (Rect);
    DrawFrameControl (Grid.Canvas.Handle, DrawRect, DFC_BUTTON,
      ISChecked[Column.Field.AsInteger]);  // Don't know what ISChecked is 
   end;

   // The below should no longer be needed, because DefaultDrawColumnCell has
   // been called by the generic handler already.
   //
   // else 
   //  Grid.DefaultDrawColumnCell (Rect, DataCol, Column, State);
end;

After seeing this comment you made to Sertac:

in one grid, it could be column 3 whereas it could be column 4 which needs to be drawn as a checkbox.

I've offered one way to address that in my code above (see the comment in GenericDrawColumnCell). An alternative (provided you only have one column in each grid that needs a checkbox) is to indicate the column that contains the checkbox in the TDBGrid.Tag property:

if (Column.Index = Grid.Tag) then

Hope this can help to fellow c++ builder XE users too using native TDBGrid. Code below adds a checkbox image in TDBGrid column.


void GridCheckbox( TColumn *Column, const TRect &Rect, bool isChecked, bool isCentered)
{
    
    //use or call this function inside OnDrawColumnCell
       
    TDBGrid *grd = (TDBGrid*)Column->Grid;

    String sImg;
    if (isChecked){   // record current status if checked or unchecked      
        sImg = "" + checkbox_on.bmp"; 
    }else{
        sImg = "" + checkbox_off.bmp";
    }

    if ( FileExists(sImg) ){

        TBitmap *img;
        img = new TBitmap();
        img->Transparent = True;
        img->TransparentColor = img->Canvas->Brush->Color;
        // img->TransparentMode = tmAuto;
        img->LoadFromFile(sImg);

        int ntLeft = Rect.Left + 5;
        if ( isCentered ) ntLeft = Rect.Left + ( (Rect.Width() / 2) - (img->Width / 2) ) ;

        grd->Canvas->Draw(ntLeft, Rect.Top + 2,img);

        delete img;
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top