2005. február 26., szombat

Select multiple rows in a TStringGrid


Problem/Question/Abstract:

Is there any way to allow the user to select multiple rows that are not consecutive for example selecting rows 1,4,5,13, and 16 but not 2,3,6,7,8... ?

Answer:

The standard selection mechanism build into the stringgrid only supports one consecutive block of selected cells (via the selection property). If you want more you have to code it. Find a way to store the selected state on a per-cell or per-row basis and then use the mouse events to give the user a means to change the state and a OnDrawCell handler to draw the cells accordingly.

I used the fixed column 0 in this grid to store the selected state for a row, the cell is either empty (not selected) or contains a space character (selected). I choose to draw a selection marker in column 0 instead of painting the selected rows with another background/ foreground color in this app. Something missing is the ability to click on the fixed column cell to toggle the selected state. I never got around to add that, you would use MouseToCell in OnMouseUp for that and make sure the Row property is set to that cells row before calling ToggleSelection. More work needs to be invested to support range selections as well.

const
  sRowSelected = ' ';
  sRowNotSelected = #0;

  {This method is attached to the OnKeyPress event handler for the SGridIndications object. We use it to implement selection/ deselection of rows in the grid by a press of the spacebar. The grid is otherwise read-only.}

procedure TEinsendeMainForm.SGridIndicationsKeyPress(Sender: TObject; var Key: Char);
begin
  if key = ' ' then
    ToggleSelectedState;
  key := #0;
end;

{This method is attached to the OnMouseUp event handler for the SGridIndications object. We use it to implement selection/deselection of rows in the grid by a click of the mouse. Any mouse button can be used.}

procedure TEinsendeMainForm.SGridIndicationsMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  ToggleSelectedState;
end;

{procedure TEinsendeMainForm.SGridIndicationsDrawCell

Parameters:
Sender: the SGridIndications object
Col: column index for cell to draw
Row: row index for cell to draw
Rect: cell rectangle
State: cell state

Call method: static

Description:
This method is attached to the grids OnDrawCell event that is called every time a
cell needs to be draw. We rely mostly on the default drawing done by the grid
to display the data.The only thing we add is for the fixed column 0 cells:
if the cell contains a blank as a marker that it is selected we draw a red triangle as a visual cue for the selected state for this row. This type of selection is independend of the standard row selection the grid is set up for and allows any
number of rows to be selected and deselected individually. The grid does not support this kind of selection directly.

Error Conditions: none

Created: 29.06.97 11:58:58 by Peter Below
}

procedure TEinsendeMainForm.SGridIndicationsDrawCell(Sender: TObject;
  Col, Row: Longint; Rect: TRect; State: TGridDrawState);
var
  poly: array[1..3] of TPoint;
  dy, dx: Integer;
begin
  if (Col = 0) and ((Sender as TStringGrid).Cells[0, Row] = ' ') then
  begin
    with TStringGrid(Sender).Canvas do
    begin
      Brush.Color := clRed;
      Pen.Color := clRed;
      poly[1].X := Rect.Right - 5;
      poly[1].Y := (Rect.Bottom - Rect.Top) div 2 + Rect.Top;
      dy := (Rect.Bottom - Rect.Top) * 6 div 10;
      dx := Round(Sqrt(3 * dy * dy) / 2);
      poly[2].X := poly[1].X - dx;
      poly[3].X := poly[2].X;
      poly[2].Y := poly[1].Y - dy div 2;
      poly[3].Y := poly[1].Y + dy div 2;
      Polygon(poly);
    end;
  end;
end;

{function TEinsendeMainForm.CountSelectedIndications
Parameters: none

Returns:
the number of rows currently marked as selected in the indication grid.

Call method: static

Description:
Iterates over all rows of the grid and checks the content of column 0.

Error Conditions: none

Created: 29.06.97 13:42:31 by P. Below
}

function TEinsendeMainForm.CountSelectedIndications: Integer;
var
  i: Integer;
begin
  Result := 0;
  with SGridIndications do
    for i := 1 to RowCount - 1 do
      if Cells[0, i][1] = sRowSelected then
        Inc(Result);
end;

{procedure TEinsendeMainForm.UnselectAllIndications
|
Parameters: none

Call method: static

Description: Deselects all indications in the indication grid.

Error Conditions: none

Created: 07.07.97 13:28:28 by P. Below
}

procedure TEinsendeMainForm.UnselectAllIndications;
var
  i: Integer;
begin
  with sGridIndications do
    for i := 1 to RowCount - 1 do
      Cells[0, i] := sRowNotSelected;
end;

{procedure TEinsendeMainForm.ToggleSelectedState

Parameters: none

Call method: static

Description:
Inverts the selection state of the current row in the indication grid. Called by several event handlers for the grid.

Error Conditions: none

Created: 29.06.97 13:44:28 by P. Below
}

procedure TEinsendeMainForm.ToggleSelectedState;
begin
  with SGridIndications do
    if Cells[0, row] = sRowSelected then
      Cells[0, row] := sRowNotSelected
    else
      Cells[0, row] := sRowSelected;
  LblNumSelIndications.Caption := IntToStr(CountSelectedIndications) +
    sIndicationsSelected;
end;

{procedure TEinsendeMainForm.SelectIndication

Parameters:
IndicationCode: the numeric (Prisma) code for the indication
state: new state (selected or unselected) to set.

Call method: static

Description: Searches thru the indication grid for the indication and sets it state, if found.

Error Conditions: none

Created: 07.07.97 13:31:41 by P. Below
}

procedure TEinsendeMainForm.SelectIndication(const IndicationCode: string; state:
  Boolean);
var
  i: Integer;
  ch: Char;
begin
  with sGridIndications do
  begin
    i := Cols[1].IndexOf(IndicationCode);
    if i > 0 then
    begin
      if state then
        ch := sRowSelected
      else
        ch := sRowNotSelected;
      Cells[0, i] := ch;
    end;
  end;
end;

Nincsenek megjegyzések:

Megjegyzés küldése