Delphi 7: Как я могу изменить цвета отдельных ячеек в StringGrid, щелкнув по ним?

#delphi #delphi-7

#delphi #delphi-7

Вопрос:

Я пытаюсь создать приложение, в котором ячейки a TStringGrid будут менять цвет, когда я нажимаю на них. Каждый раз, когда я нажимаю на ячейку, она должна переключаться на следующий цвет и оставаться таким цветом, пока я не нажму на эту ячейку снова, по порядку:

белый ==> красный ==> оранжевый ==> зеленый ==> белый (как светофор).

Ошибки, которые я получаю, немного сложно объяснить, но я постараюсь.

Приложение запускается, но когда я нажимаю на одну ячейку, а затем на другую, иногда первая ячейка, на которую я нажал, меняет цвет, а вторая — нет. В других случаях обе ячейки меняют цвет. В других случаях обе ячейки просто сбрасываются в свое белое состояние.

 type
  TForm1 = class(TForm)
    StringGrid: TStringGrid;
    procedure StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
  private
    arrState: array[1..4, 1..4] of Integer;
end;

procedure TForm1.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  iRow, iCol: Integer;
  arrk: array[1..4, 1..4] of Integer;
begin
  for iCol := 4 downto 1 do
  begin
    for iRow := 4 downto 1 do
    begin
      if (gdSelected in State) then
      begin
        case arrState[ARow, aCol] of
           0: begin
             StringGrid.Canvas.Brush.Color := clWhite;
             Rect := StringGrid.CellRect(iCol, iRow);
             StringGrid.Canvas.FillRect(Rect);
             Inc(arrState[iRow, iCol]);
           end;

           1: begin
             StringGrid.Canvas.Brush.Color := clRed;
             Rect := StringGrid.CellRect(iCol, iRow);
             StringGrid.Canvas.FillRect(Rect);
             Inc(arrState[iRow, iCol]);
           end;

           2: begin
             StringGrid.Canvas.Brush.Color := $008CFF;
             Rect := StringGrid.CellRect(iCol, iRow);
             StringGrid.Canvas.FillRect(Rect);
             Inc(arrState[iRow, iCol]);
           end;

           3: begin
             StringGrid.Canvas.Brush.Color := clGreen;
             Rect := StringGrid.CellRect(iCol, iRow);
             StringGrid.Canvas.FillRect(Rect);
             arrState[iRow, iCol] := 0;
           end;

         end;
       end;
     end;
   end;
 end;
 

Ответ №1:

Проблема в том, что вы используете OnDrawCell событие для обновления вашего конечного автомата. НИКОГДА не используйте событие рисования для изменения состояния! Элемент управления пользовательского интерфейса часто и по многим причинам окрашивается, поэтому любые события рисования должны отображать только текущее состояние для конкретного элемента, который в данный момент рисуется. Вы должны использовать OnSelectCell OnClick событие or для обновления вашего конечного автомата, а затем запустить перерисовку обновленной ячейки.

Попробуйте это:

 type
  TForm1 = class(TForm)
    StringGrid: TStringGrid;
    procedure StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure StringGridSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
  private
    arrState: array[1..4, 1..4] of Integer;
end;
 
 procedure TForm1.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
const
  clOrange = TColor($008CFF);
  CellColors: array[0..3] of TColor = (clWhite, clRed, clOrange, clGreen);
begin
  if (ACol in [1..4]) and (ARow in [1..4]) then
  begin
    StringGrid.Canvas.Brush.Color := CellColors[arrState[ARow, ACol]];
    StringGrid.Canvas.FillRect(Rect);
  end;
end;

// TStringGrid.InvalidateCell() is protected,
// but can be reached using an accessor class..
type
  TStringGridAccess = class(TStringGrid)
  end;

procedure TForm1.StringGridSelectCell(Sender: TObject; ACol, ARow: Integer;
  var CanSelect: Boolean);
begin
  if (ACol in [1..4]) and (ARow in [1..4]) then
  begin
    arrState[ARow, ACol] := (arrState[ARow, ACol]   1) mod 4;
    TStringGridAccess(StringGrid).InvalidateCell(ACol, ARow);
  end;
end;
 

В качестве альтернативы:

 type
  TForm1 = class(TForm)
    StringGrid: TStringGrid;
    procedure StringGridClick(Sender: TObject);
    procedure StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
  private
    arrState: array[1..4, 1..4] of Integer;
  end;
 
 // TStringGrid.InvalidateCell() is protected,
// but can be reached using an accessor class..
type
  TStringGridAccess = class(TStringGrid)
  end;

procedure TForm1.StringGridClick(Sender: TObject);
type
  POINTS = packed record
    x: SHORT;
    y: SHORT;
  end;
var
  dwPos: DWORD;
  pts: POINTS absolute dwPos;
  pt: TPoint;
  iCol, iRow: Integer;
begin
  dwPos := GetMessagePos();
  pt := StringGrid.ScreenToClient(Point(pts.x, pts.y));
  StringGrid.MouseToCell(pt.X, pt.Y, iCol, iRow);
  if (iCol in [1..4]) and (iRow in [1..4]) then
  begin
    arrState[iRow, iCol] := (arrState[iRow, iCol]   1) mod 4;
    TStringGridAccess(StringGrid).InvalidateCell(iCol, iRow);
  end;
end;

procedure TForm1.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
const
  clOrange = TColor($008CFF);
  CellColors: array[0..3] of TColor = (clWhite, clRed, clOrange, clGreen);
begin
  if (ACol in [1..4]) and (ARow in [1..4]) then
  begin
    StringGrid.Canvas.Brush.Color := CellColors[arrState[ARow, ACol]];
    StringGrid.Canvas.FillRect(Rect);
  end;
end;