Disjunct row selection and setting check boxes

I have to think there is some combination of options I must set and event handlers to hook to do what I need. But at this point it is eluding me.


I have a TAdvStringGrid and the disjunct row selection is working well. Having selected a number of rows (not necessarily adjacent), I want to click on the check box in one column and for it to apply the new state across all of the selected rows (for example to turn on/off the drawing of a number of curves). Unfortunately when I click on the check box is un-selects all of the selected rows before then setting the state of the single check box I clicked on.

I have come up with some complicated schemes of maintain selection state information. Unfotunately the order of firing of the various events of OnMouseUp, OnMouseDown, OnRowDisjunctSelect, OnRowDisjunctSelected, OnCheckBoxChange, OnCheckBoxMouseUp etc. have eluded me.

I have beating my head against this for a couple of days and have no tried so many combinations I am sure I am repeating myself.

I was hoping someone can put me straight here.

Hi Martel,

  if i well understood ... you have some selected rows like row 1,6 15,25 ....and so on.
Each row has a  column with a checkbox.

Your goal is set check value on a selected row and apply this value to all the selected rows in one shot.
If this is the matter, what you can do is work on AdvStringGrid1CheckBoxChange(Sender: TObject; ACol,  ARow: Integer; State: Boolean);

Here you can check the state of the checkbox row and with a loop form 1 to lastrow you can set all the other selectetd row as, for example :

if grid.IsSelected(ACol,CurrentRowloopindex) then
begin
   // The row is selected, set checkboxstate
  grid.SetCheckBoxState(Col,Row,State) // Where state came as procedure parameter
end;

Not tested, but could work ... (i did it in an old project ....)

Regards
Daniele

Daniele,


Thanks for the suggestion. 

Yes you would think it would work, but it does not  (First thing I tried). The problem is that as soon as you click on the check box (before the onCheckBoxChange fires) the grid code says... 

"hey you have clicked on a row so I am going to deselect all the other selected rows and simply select the one you clicked on"

Only then does it fire the OnCheckboxChange.

I wrote a small test harness last night and hooked all the possible events and looked for a path that would work. I have just got it going this morning. There may yet be other ways that don't need this additional buffering of selected rows, but for now it is working for me [by the way the other goal was that after clicking the check box, the selected rows are still selected, because there are other columns I may want to set].

So for now I have this

//Private form variables
    MultiSelList: TList<Integer>;
    ChkChangedTo: boolean;
    ChkChangeColumn: Integer;
    ChkChanged: boolean;

//OnCanClickCell Handler
procedure TForm1.tCrvGrdCanClickCell(Sender: TObject; ARow, ACol: Integer; var Allow: Boolean);
var
  i: Integer;
begin
  ChkChanged:= false;
 //Cols 0 and 2 are check box columns
  if (ACol = 0) or (ACol = 2) then
  begin
    MultiSelList.Clear;
    if not tCrvGrd.RowSelect[ARow] then
      MultiSelList.Add(ARow)
    else
    begin
      for i:= 1 to tCrvGrd.RowCount-1 do
      begin
        if tCrvGrd.RowSelect then
          MultiSelList.Add(i);
      end;
    end;
  end;
end;

//OnCheckCoxChange handler
procedure TForm1.tCrvGrdCheckBoxChange(Sender: TObject; ACol, ARow: Integer; State:Boolean);
begin
  ChkChangedTo:= State;
  ChkChangeColumn:= ACol;
  ChkChanged:= true;
end;

//OnCheckBoxMouseUp handler
procedure TForm1.tCrvGrdCheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;  State:Boolean);
var
  i: Integer;
begin
  if ChkChanged then
  begin
    //Here is where we do the rest of the changing of check boxes and possibly
    //  re-selecting rows
    for i:= 0 to MultiSelList.Count-1 do
    begin
      //Set the check box state for the rows that were selected before the mouse click deselected them
      tCrvGrd.SetCheckBoxState(ChkChangeColumn, MultiSelList, ChkChangedTo);
      //Reselect the rows that were selected before the mouse click deselected them
      tCrvGrd.RowSelect[MultiSelList]:= true;
    end;
    ChkChangedTo:= State;
    ChkChangeColumn:= ACol;
  end;
  ChkChanged:= false;
  MultiSelList.Clear;
end;

Hi Martel,

i search in my old project and fount it !!!!

I have isolated a piece of code that do what you search .... it work at 90% ... the last 10% is your headache eheheheh.



Limit ... it's work with delphi Seattle and old Component pack (because i'm on that pc) with advstringlist (not latest version).



Here the unit



unit Unit1;



interface



uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvUtil, Vcl.Grids, AdvObj, BaseGrid,

AdvGrid, Vcl.StdCtrls;



type

TForm1 = class(TForm)

    SG1: TAdvStringGrid;

    procedure FormCreate(Sender: TObject);

    procedure SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

private

    { Private declarations }

public

    { Public declarations }

end;



var

Form1: TForm1;



implementation



{$R *.dfm}



procedure TForm1.FormCreate(Sender: TObject);

Var I : Byte;

begin

SG1.ClearAll;

SG1.ColCount:=6;

SG1.RowCount:=1;

for I:=1 to 50 Do

Begin

    SG1.AddRow;

    SG1.Ints[1,I]:=I;

    SG1.AddCheckBox(2,I,False,False);

End;



end;



procedure TForm1.SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

Var state : Boolean;

    I     : Integer;

    SL    : TStringList;

begin

if ACol=2 then

begin

    SL:=TStringList.Create;

    for i := 0 to SG1.SelectedRowCount - 1 do

    Begin

      // Copy selected row index

      SL.Add(IntToStr(SG1.SelectedRow));

    End;

    if SG1.GetCheckBoxState(ACol,Arow,State) then SG1.SetCheckBoxState(ACol,ARow, Not state);

    for I:=0 to SL.Count-1 do

    begin

       SG1.RowSelect[StrToint(SL)]:=True;

       SG1.SetCheckBoxState(ACol,StrToint(SL), Not state);

    End;

    FreeAndNil(SL);

end;

end;



end.



Setting: in order to work (90%) please set in grid option

goRowSelect:=True

and in mouseAction

DisjunctRowSelect:=True



In this scenario, you can check/uncheck any selected row on it's checkbox and the state is applyed to all each other rows.

Why work only 90%?

Because the row where you click change the selected status ..... but with e few code lines i'm sure you can fix this and made it selectable ...



To save your time ... i'm adjust the above to the follow one and now work 100%



unit Unit1;



interface



uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvUtil, Vcl.Grids, AdvObj, BaseGrid,

AdvGrid, Vcl.StdCtrls;



type

TForm1 = class(TForm)

    SG1: TAdvStringGrid;

    procedure FormCreate(Sender: TObject);

    procedure SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

    procedure SG1CheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;

      State: Boolean);

    procedure FormClose(Sender: TObject; var Action: TCloseAction);

private

    { Private declarations }



    SL : TStringList;

public

    { Public declarations }

end;



var

Form1: TForm1;



implementation



{$R *.dfm}



procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

FreeAndNil(SL);

end;



procedure TForm1.FormCreate(Sender: TObject);

Var I : Byte;

begin

SL:=TStringlist.Create;

SG1.ClearAll;

SG1.ColCount:=6;

SG1.RowCount:=1;

for I:=1 to 50 Do

Begin

    SG1.AddRow;

    SG1.Ints[1,I]:=I;

    SG1.AddCheckBox(2,I,False,False);

End;

end;



procedure TForm1.SG1CheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;

State: Boolean);

begin

if SL.IndexOf(IntToStr(ARow))<>-1 then

Begin

    SG1.RowSelect[Arow]:=True;

End;

end;



procedure TForm1.SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

Var state : Boolean;

    I     : Integer;

begin

if ACol=2 then

begin

    SL.Clear;

    for i := 0 to SG1.SelectedRowCount - 1 do

    Begin

      // Copy selected row index

      SL.Add(IntToStr(SG1.SelectedRow));

    End;

    if SG1.GetCheckBoxState(ACol,Arow,State) then SG1.SetCheckBoxState(ACol,ARow, Not state);

    for I:=0 to SL.Count-1 do

    begin

       SG1.RowSelect[StrToint(SL)]:=True;

       SG1.SetCheckBoxState(ACol,StrToint(SL), Not state);

    End;

end;

end;



end.



As you know well be aware about keyboard/mouse action because you can loose all your selected row if you release the crtl key.

I'm sure this code will be improved, but i'm hope this can help you and be a starting point.



Regard

Daniele

Daniele,


Thank you for your time looking out this stuff. I always appreciate the time put in trying to help others.

Not sure if you saw my post yesterday. I got this working, and posted my working code.(See post above yours)

I have taken a look at your code, particularly the code you say is 100%. Did you actually compile this?

There seem to be several lines that I don't believe could compile in any version of Delphi.

In 
procedure TForm1.SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

the line 
      SL.Add(IntToStr(SG1.SelectedRow));

cannot compile as SelectedRow is an indexed property my guess is your intention was 

      SL.Add(IntToStr(SG1.SelectedRow));

Just following that is

are several lines where you try to convert the whole string list to an integer, e.g. 

      SG1.RowSelect[StrToint(SL)]:=True;

StrToint(SL) can not work

Even after converting all the index properties the way I believe you intended the code still does not work for me in Delphi 10.1 (Berlin). This issue I describe is as before At the point your OnClickCell handler is called the String grid has already deselected the rows.


Anyway as I said I have my code working now and it is posted yesterday. I thank you for your effort

Hi Martel, 

   you're rigth !!!

Now i'm on my house pc with delphi rio.

There are some errors .... due to unknow reason (i'm just copy the code into the forum).

Anyway, i'm glad you have already solved the problem but, only to fix the above code, here the all little project full running on rio

 

unit Unit1;

 

interface

 

uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvUtil, Vcl.Grids, AdvObj, BaseGrid,

AdvGrid, Vcl.StdCtrls;

 

type

TForm1 = class(TForm)

    SG1: TAdvStringGrid;

    procedure FormCreate(Sender: TObject);

    procedure SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

    procedure SG1CheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;

      State: Boolean);

    procedure FormClose(Sender: TObject; var Action: TCloseAction);

private

    { Private declarations }

 

    SL : TStringList;

public

    { Public declarations }

end;

 

var

Form1: TForm1;

 

implementation

 

{$R *.dfm}

 

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  FreeAndNil(SL);

end;

 

procedure TForm1.FormCreate(Sender: TObject);

Var I : Byte;

begin

  SL:=TStringlist.Create;

  SG1.ClearAll;

  SG1.ColCount:=6;

  SG1.RowCount:=1;

  for I:=1 to 50 Do

  Begin

    SG1.AddRow;

    SG1.Ints[1,I]:=I;

    SG1.AddCheckBox(2,I,False,False);

  End;

end;

 

procedure TForm1.SG1CheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;

State: Boolean);

var I : Integer;

begin

  if SL.IndexOf(IntToStr(ARow))<>-1 then

  Begin

    SG1.RowSelect[Arow]:=True;

  End;

end;

 

procedure TForm1.SG1ClickCell(Sender: TObject; ARow, ACol: Integer);

Var state : Boolean;

    I     : Integer;

begin

  if ACol=2 then

  begin

    SL.Clear;

    for i := 0 to SG1.SelectedRowCount - 1 do

    Begin

      // Copy selected row index

      SL.Add(IntToStr(SG1.SelectedRow));

    End;

    if SG1.GetCheckBoxState(ACol,Arow,State) then SG1.SetCheckBoxState(ACol,ARow, Not state);

    for I:=0 to SL.Count-1 do

    begin

       SG1.RowSelect[StrToint(SL)]:=True;

       SG1.SetCheckBoxState(ACol,StrToint(SL), Not state);

    End;

  end;

end;

 

end.

 

I think that working on internal row indexs (RowSelect and SelectedRow) is possible delete the stringlist variable.

As said before, this just for “errata corrige”

 

Excuse me for any inconvenients.

 

Regards

Daniele

Hi,

 once again ... paste problem !
The code in the forum is not the code in the ide.

Some lines are broken.
I try to wite only the procedure 

procedure TForm1.SG1ClickCell(Sender: TObject; ARow, ACol: Integer);
Var state : Boolean;
    I     : Integer;
begin
  if ACol=2 then
  begin
    SL.Clear;
    for i := 0 to SG1.SelectedRowCount - 1 do
    Begin
      // Copy selected row index
      SL.Add(IntToStr(SG1.SelectedRow[I]));
    End;
    if SG1.GetCheckBoxState(ACol,Arow,State) then SG1.SetCheckBoxState(ACol,ARow, Not state);
    for I:=0 to SL.Count-1 do
    begin
       SG1.RowSelect[StrToint(SL[I])]:=True;
       SG1.SetCheckBoxState(ACol,StrToint(SL[I]), Not state);
    End;
  end;

Hope this is the rigth one...

Daniele
end;

procedure TForm1.SG1CheckBoxMouseUp(Sender: TObject; ACol, ARow: Integer;
State: Boolean);
var I : Integer;
begin
  if SL.IndexOf(IntToStr(ARow))<>-1 then
  Begin
    SG1.RowSelect[Arow]:=True;
  End;
end;

procedure TForm1.FormCreate(Sender: TObject);
Var I : Byte;
begin
  SL:=TStringlist.Create;
  SG1.ClearAll;
  SG1.ColCount:=6;
  SG1.RowCount:=1;
  for I:=1 to 50 Do
  Begin
    SG1.AddRow;
    SG1.Ints[1,I]:=I;
    SG1.AddCheckBox(2,I,False,False);
  End;
end;







Martel,

  there are a bbcodes problems ..... in the code
Into loop for I:=0 to xx there is a bbcode !!! 

For next reply i'll avoid I,U,B variables for array !!!

Daniele

Please just stop replying. It is not needed and will confuse the whole topic.


It is fixed it works for me.

Enough, please