#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;