1

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?

Community
  • 1
  • 1
No'am Newman
  • 6,395
  • 5
  • 38
  • 50
  • Is it possible to define the general handler with an extra parameter?" No, because the general handler has to meet the definition of a `TDrawColumnCellEvent` handler. – Ken White Feb 28 '14 at 05:25
  • @KenWhite: Probably I could use the above code, then call your code with the suitable parameters in order to get the background set correctly. But how can I generalise the checkbox code? – No'am Newman Feb 28 '14 at 05:40
  • I'm not sure what you're asking. Do you mean "Once I've done the code inside the `if` statement, can I also call a more generic OnDrawColumnCell handler afterward?"? Or are you asking "Regardless whether the `if` statement executes or not, can I call a more generic OnDrawColumCell handler as well?"? – Ken White Feb 28 '14 at 05:54
  • @KenWhite: 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. – No'am Newman Feb 28 '14 at 09:00
  • You already have the column index, no? – Sertac Akyuz Feb 28 '14 at 11:04
  • @SertacAkyuz: in one grid, it could be column 3 whereas it could be column 4 which needs to be drawn as a checkbox. – No'am Newman Feb 28 '14 at 16:22
  • @No'am - Ok, then you don't want 'column index', you want some 'ColumnWithCheckbox' variable. Try to integrate it with the grid, say the 'tag' property, then extract it from the 'Sender'. You can of course derive your own grid. Failing that, you can use a global variable. – Sertac Akyuz Feb 28 '14 at 16:35
  • @SertacAkyuz: That's exactly the conclusion that I reached. I'm presenting my code as an answer. – No'am Newman Feb 28 '14 at 19:31

3 Answers3

1

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
Ken White
  • 123,280
  • 14
  • 225
  • 444
1

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.

No'am Newman
  • 6,395
  • 5
  • 38
  • 50
0

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;
    }

}
Allan.A
  • 27
  • 6