title: Checkboxes in StringGrid do not toggle normally: Delphi
body: <p>I've modified the Delphi code found <a href="http://delphi.about.com/od/vclusing/l/aa072203b.htm" rel="nofollow">here</a> to save the checkbox state in a three column stringgrid. The problem is that you have to click a cell twice in order to toggle it. If I set goEditing then you can set the state with one click but one more click makes the checkbox invisible. How can I prevent these edit state problems?</p>
  
  <pre><code>unit Unit1;
  
  interface
  
  uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, Grids;
  
  type
    TForm1 = class(TForm)
      gridOwnerDraw: TStringGrid;//must set goEditing True
      procedure gridOwnerDrawDrawCell(Sender: TObject; ACol, ARow: Integer;
        Rect: TRect; State: TGridDrawState);
      procedure gridOwnerDrawClick(Sender: TObject);
      procedure FormResize(Sender: TObject);
    private
      { Private declarations }
      FInMouseClick: boolean;
      function GetBtnRect(ACol, ARow: integer; complete: boolean): TRect;
    public
      { Public declarations }
    end;
  
  var
    Form1: TForm1;
  
  implementation
  
  {$R *.dfm}
  
  const
    TXT_MARG: TPoint = (x: 4; y: 2);
    BTN_WIDTH = 12;
  var
    Checked1: array[1..4] of boolean = (false, true, false, true);
    Checked2: array[1..4] of boolean = (true, false, true, false);
  
  //Returns rectangle where button will be drawn:
  procedure TForm1.FormResize(Sender: TObject);
  begin
    gridOwnerDraw.Invalidate;
  end;
  
  function TForm1.GetBtnRect(ACol, ARow: integer; complete: boolean): TRect;
  
    function MakeBtnRect(Alignment: TAlignment; cellrect: TRect; complete: boolean): TRect;
    var
      rowHeight: integer;
    begin
      result := cellrect;
      rowheight := cellrect.bottom - cellrect.top;
  
      case Alignment of
        taLeftJustify:
          begin
            result.Right := cellrect.left + BTN_WIDTH + TXT_MARG.x + (TXT_MARG.x div 2);
            if not complete then
            begin
              result.Top := cellrect.Top + ((RowHeight - BTN_WIDTH) div 2);
              result.Left := cellrect.Left + ((RowHeight - BTN_WIDTH) div 2);
              result.Bottom := result.Top + BTN_WIDTH;
              result.Right := result.Left + BTN_WIDTH;
            end;
          end;
        taRightJustify:
          begin
            result.Left := cellrect.Right - BTN_WIDTH - TXT_MARG.x - TXT_MARG.x;
            if result.left &lt; cellrect.left then
              result.left := Cellrect.left;
  
            if not complete then
            begin
              result.top := cellrect.top + ((RowHeight - BTN_WIDTH) div 2);
              result.left := result.left + TXT_MARG.x;
              result.right := Result.left + BTN_WIDTH;
              result.Bottom := result.top + BTN_WIDTH;
            end;
          end;
        taCenter:
          begin
            result.left := result.left + ((cellrect.Right - cellrect.left) div 2) - (BTN_WIDTH div 2) - TXT_MARG.x;
            if result.left &lt; cellrect.Left then
              result.left := cellrect.left;
            result.right := result.left + BTN_WIDTH + TXT_MARG.x + TXT_MARG.x;
            if not complete then
            begin
              result.Top := cellrect.Top + ((RowHeight - BTN_WIDTH) div 2);
              result.Left := result.Left + TXT_MARG.x;
              result.Bottom := result.Top + BTN_WIDTH;
              result.Right := result.Left + BTN_WIDTH;
            end;
          end;
      end;
    end;
  
  var
    cellrect: TRect;
  begin
    result := Rect(0, 0, 0, 0);
  
    //Get complete cellrect for the current cell:
    cellrect := gridOwnerDraw.CellRect(ACol, ARow);
  
    //Last visible row sometimes get truncated so we need to fix that
    if (cellrect.Bottom - cellrect.Top) &lt; gridOwnerDraw.DefaultRowHeight then
      cellrect.Bottom := cellrect.top + gridOwnerDraw.DefaultRowheight;
  
    if ARow &gt; 0 then
    begin
      //Additional lines have two buttons:
      case ACol of
        1: result := MakeBtnRect(taCenter, cellrect, complete);
        2: result := MakeBtnRect(taCenter, cellrect, complete);
      end;
    end;
  end;
  
  procedure TForm1.gridOwnerDrawClick(Sender: TObject);
  var
    where: TPoint;
    ACol, ARow: integer;
    btnRect: TRect;
  begin
    //Again, check to avoid recursion:
    if not FInMouseClick then
    begin
      FInMouseClick := true;
      try
        //Get clicked coordinates and cell:
        where := Mouse.CursorPos;
        where := gridOwnerDraw.ScreenToClient(where);
        gridOwnerDraw.MouseToCell(where.x, where.y, ACol, ARow);
        if ARow &gt; 0 then
        begin
          //Get buttonrect for clicked cell:
          btnRect := GetBtnRect(ACol, ARow, false);
          InflateRect(btnrect, 2, 2);  //Allow 2px 'error-range'...
  
          //Check if clicked inside buttonrect:
          if PtInRect(btnRect, where) then
          begin
            case ACol of
              1: Checked1[ARow]:= Not Checked1[ARow];
              2: Checked2[ARow]:= Not Checked2[ARow];
            end;
          end;
        end;
      finally
        FInMouseClick := false;
      end;
    end;
  end;
  
  procedure TForm1.gridOwnerDrawDrawCell(Sender: TObject; ACol, ARow: Integer;
    Rect: TRect; State: TGridDrawState);
  var
    txtRect: TRect;
    btnRect: TRect;
    btnState: integer;
    focusRect: TRect;
  begin
    //If header is to be drawn:
    if ARow = 0 then
    begin
    end
  
    //For the rest of the rows:
    else
    begin
      //Setting canvas properties and erasing old cellcontent:
      gridOwnerDraw.Canvas.Brush.Color := clWindow;
      gridOwnerDraw.Canvas.Brush.Style := bsSolid;
      gridOwnerDraw.Canvas.Pen.Style := psClear;
      gridOwnerDraw.Canvas.FillRect(rect);
  
      //Textposition:
      txtRect := Rect;
      focusRect := Rect;
      if ACol = 1 then
      begin
        txtRect.Left := Rect.left + BTN_WIDTH + TXT_MARG.x + TXT_MARG.x;
        focusRect.Left := txtRect.Left;
      end
      else if ACol = 2 then
      begin
        txtRect.Left := Rect.left + TXT_MARG.x;
      end;
  
      //Drawing selection:
      gridOwnerDraw.Canvas.Font.Style := [];
      if (gdSelected in State) then
      begin
        gridOwnerDraw.Canvas.Brush.Color := clbtnFace;
        gridOwnerDraw.Canvas.Font.Color := clBlue;
      end
      else
      begin
        gridOwnerDraw.Canvas.Brush.Color := clWindow;
        gridOwnerDraw.Canvas.Font.Color := clWindowText;
      end;
      gridOwnerDraw.canvas.FillRect(Rect);
  
      //Drawing buttons:
      if ACol &gt; 0 then
      begin
        //Clear buttonarea:
        btnRect := GetBtnRect(ACol, ARow, true);
        gridOwnerDraw.canvas.Brush.Color := clWindow;
        gridOwnerDraw.canvas.FillRect(btnrect);
  
        //Get buttonposition and draw checkbox:
        btnRect := GetBtnRect(ACol, ARow, false);
        btnState := DFCS_BUTTONCHECK or DFCS_FLAT;
        if (ACol=1) and Checked1[ARow] then
          btnState := btnState or DFCS_CHECKED
        else if (ACol=1) then
          btnState := btnState or DFCS_BUTTONCHECK
        else if (ACol=2) and Checked2[ARow] then
          btnState := btnState or DFCS_CHECKED
        else if (ACol=2) then
          btnState := btnState or DFCS_BUTTONCHECK;
        DrawFrameControl(gridOwnerDraw.canvas.handle, btnRect, DFC_BUTTON, btnState)
      end;
  
      //If selected, draw focusrect:
      if gdSelected in State then
      begin
        gridOwnerDraw.canvas.pen.Style := psInsideFrame;
        gridOwnerDraw.canvas.pen.Color := clBtnShadow;
        gridOwnerDraw.canvas.Polyline([Point(focusRect.left-1, focusRect.Top), Point(focusRect.right-1, focusRect.Top)]);
        gridOwnerDraw.canvas.Polyline([Point(focusRect.left-1, focusRect.Bottom-1), Point(focusRect.right-1, focusRect.Bottom-1)]);
        if ACol = 1 then
          gridOwnerDraw.canvas.Polyline([Point(focusRect.left-1, focusRect.Top), Point(focusRect.left-1, focusRect.Bottom-1)])
        else if ACol = gridOwnerDraw.ColCount - 1 then
          gridOwnerDraw.canvas.Polyline([Point(focusRect.right-1, focusRect.Top), Point(focusRect.right-1, focusRect.Bottom-1)]);
      end;
    end;
  end;
  
  end.
  </code></pre>