Recent

Author Topic: [SOLVED] I'm stumped: Enforcing a unique checkbox in TStringGrid  (Read 496 times)

EganSolo

  • Sr. Member
  • ****
  • Posts: 380
Sample Project attached.

//ADDENDUM: I've managed to solve this issue (see my latest reply). The attached project contains the attempt I describe here commented out (in case you'd like to see for yourself) and the actual solution.
//

I want to enforce no more than one checkbox being checked in a TStringGrid, where the column's button style is set to cbsCheckBoxColumn. If a box is already checked and the user clicks on another cell, then the checked box must be unchecked.

I've checked that the column with the checkboxes is not read-only. The Grid's options goAlwaysShowEditor and goEditing are set to true.

I've declared a private attribute in TForm1 as follows:
Code: Pascal  [Select][+][-]
  1.   private
  2.     fCBRow : integer;
  3.  

In TForm1.Create, I shrink the fixed column, initialize fCBRow to -1 and clear all the checkboxes.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var aRow: integer;
  3. begin
  4.   StringGrid1.ColWidths[0] := 30;
  5.   fCBRow := -1;
  6.   with StringGrid1 do
  7.     If RowCount > 1
  8.     then for aRow := 1 to RowCount -1 do
  9.       Cells[cb_col_idx,aRow] := '0';
  10. end;
  11.  

Lastly, I've implemented StringGrid1.OnClick to enforce the restrictions.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.StringGrid1Click(Sender: TObject);
  2. begin
  3.   with StringGrid1 do
  4.   begin
  5.    If RowCount < 3 then exit;
  6.    OnClick := Nil;
  7.    If (Col = cb_col_idx)
  8.    then begin
  9.         If Row = fCBRow
  10.         then begin
  11.           Cells[Col,Row] := '0';
  12.           InvalidateCell(Col,Row);
  13.           fCBRow  := -1 ;
  14.         end
  15.         else begin
  16.           If fCBRow > -1
  17.           then begin
  18.                 Cells[Col,fCBRow] := '0';
  19.                 InvalidateCell(Col,Row);
  20.               end;
  21.           Cells[Col,Row] := '1';
  22.           InvalidateCell(Col,Row);
  23.           fCBRow := Row;
  24.         end;
  25.         Invalidate;
  26.         Repaint;
  27.    end;
  28.    OnClick := @StringGrid1Click;
  29.   end;
  30. end;
  31.  

But something is wrong: when I click on a cell containing a checkbox, the cell is focused, and the checkbox doesn't change. The OnClick event is invoked and the values are correctly updated, but the grid's visual appearance doesn't change. If I comment out the code in the OnClick event handler, the checkbox behaves correctly.

Suggestions?
« Last Edit: August 07, 2025, 06:23:29 am by EganSolo »

jamie

  • Hero Member
  • *****
  • Posts: 7323
Re: I'm stumped: Enforcing a unique checkbox in TStringGrid
« Reply #1 on: August 07, 2025, 03:31:39 am »
I didn't download your project but I can only take a guess that maybe you could try calling the REPAINT in that event.

 Or even Invalidate for the grid.

Jamie
The only true wisdom is knowing you know nothing

EganSolo

  • Sr. Member
  • ****
  • Posts: 380
Re: I'm stumped: Enforcing a unique checkbox in TStringGrid
« Reply #2 on: August 07, 2025, 05:59:37 am »
Hi Jamie,
  Yeah, that's what I thought too. I've modified the program and the listed code to include InvalidateCell, Invalidate and Repaint. I know it's overkill, but no dice -- the checked box is cleared but the new checked box is not updated.
  I'm starting to suspect a bug in TStringGrid.

EganSolo

  • Sr. Member
  • ****
  • Posts: 380
Re: I'm stumped: Enforcing a unique checkbox in TStringGrid
« Reply #3 on: August 07, 2025, 06:20:35 am »
Well, I've managed to solve it by a different implementation, which may have been the right way to do it all along. At this point, I'm unsure whether my initial implementation was misusing the OnClick event or if there's a bug lurking somewhere in TStringGrid. Be that as it may, here's how I've managed to make it work:

Code: Pascal  [Select][+][-]
  1. //Implementation of the OnSetCheckBoxState. Once more, fCBRow is an attribute that tracks the currently checked row.
  2. //If no row is checked, it's set to -1.
  3.  
  4. procedure TForm1.StringGrid1SetCheckboxState(Sender: TObject; ACol, ARow: Integer; const Value: TCheckboxState);
  5. begin
  6.   with StringGrid1 do
  7.   begin
  8.     //Are we setting the value of the currently checked box? If so, uncheck it.
  9.     If aRow = fcbRow
  10.     then begin
  11.       Cells[aCol,aRow] := '0';
  12.       fcbRow := -1;
  13.     end
  14.     else begin
  15.       If fCBRow > -1
  16.       then Cells[aCol,fCBRow] := '0';
  17.       Cells[aCol,aRow] := '1';
  18.       fcbRow := aRow;
  19.     end;
  20.   end;
  21. end;
  22.  

 

TinyPortal © 2005-2018