TStringGrid не обновляется при вызове InvalidateCol () - PullRequest
4 голосов
/ 13 августа 2010

Я хочу сделать сетку строк, чтобы отобразить какой-то вертикальный курсор, чтобы выделить текущий выбранный столбец. Итак, в MouseDown я вызываю setCurPos, затем вызываю InvalidateCol для аннулирования текущего столбца. Это вызывает DrawCell. DrawCell рисует курсор на текущем столбце.

Проблема заключается в следующем: если у меня есть больше строк в сетке, то он может отображать некоторые из них не будут видны (конечно), поэтому автоматически появится вертикальная полоса прокрутки сетки. Когда я прокручиваю вниз, чтобы увидеть строки внизу сетки, курсор в этих строках не закрашивается. Похоже, что количество нижних строк (теперь видимых на экране), в которых курсор НЕ закрашен, пропорционально количеству невидимых строк в верхней части сетки.

Если я сверну и восстановлю приложение, курсор будет красиво нарисован. Итак, очевидно, invalidateColumn () не работает.

procedure TmyGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
VAR aCol, aRow: Integer;
begin
 MouseToCell(X, Y, ACol, ARow);
 ...                                                                  
    inherited MouseDown(Button, Shift, X, Y); 
    CursorPosFocus:= ACol;                          
end;


procedure TmyGrid.setCurPos(CONST NewColumn: Integer);                 
VAR OldPos: Integer;
begin
 ...
 OldPos:= CursorPos;
 FCursorPos:= NewColumn;    
 ...
 //- This is not working:
 //InvalidateCol(OldPos);
 //InvalidateCol(NewColumn);    
 //Update;

 //- THIS WORKS:
 InvalidateGrid; 
end;


procedure TmyGrid.DrawCell(ACol, ARow: integer; ARect: TRect; AState: TGridDrawState);
Var TempRect: TRect;
begin
 inherited;
  ...

 {DRAW CURSOR}
 if CursorPos= ACol then
  begin
   TempRect.Top   := 0;
   TempRect.Left  := ARect.Left;
   TempRect.Right := ARect.Right;
   TempRect.Bottom:= ClientHeight-2;     
   Frame3D(Canvas, TempRect, $909090, $808080, 1);       
  end;
end;

Delphi 7, Win XP

1 Ответ

5 голосов
/ 21 августа 2010

Вы ничего не делаете неправильно, вы только что попали в ошибку в реализации сетки VCL, которая была в Delphi 4 VCL (у меня нет ни одного более раннего компакт-диска, чтобы проверить, но он мог даже быть в 16-битный Delphi VCL уже) и все еще с нами в Delphi 2009.

Оба метода для аннулирования всей строки или столбца делают это, вычисляя площадь ячеек, которая передается во внутренний метод InvalidateRect(). Эта область всегда начинается со столбца / строки 0 и продолжается до первой полностью невидимой строки / столбца. Совершенно очевидно, что это будет правильно работать только для некроллированной клиентской области. То, что код должен делать вместо этого, становится недействительным для последнего столбца / строки и позволяет коду в помощнике InvalidateRect() выяснить, какие ячейки действительно видны, и вычислить клиентскую область, которую необходимо аннулировать из этого.

Поскольку вы пишете свой собственный класс, вы можете легко реализовать свои собственные методы для аннулирования правильного диапазона ячеек; Я сделал то же самое много лет назад, вместе с другими методами для аннулирования нескольких столбцов, нескольких строк и целых блоков ячеек. Поскольку InvalidateRect() является приватным (и это здорово), вам нужно использовать функцию Windows API с тем же именем и вычислить прямоугольник, который будет признан недействительным, с помощью метода CellRect() или BoxRect().

Хотя InvalidateGrid() работает для вас, это действительно своего рода кувалда - это делает недействительной всю сетку, что, я думаю, не то, что вы хотели, когда вы начали использовать InvalidateCol().

Для ваших экспериментов вы должны сделать циклы рисования для каждой ячейки легко видимыми. Вызывать изменение цвета фона ячеек при каждом обновлении - это простой способ убедиться, что вы действительно выполняете только минимальные перерисовки экрана. Что-то вроде

StringGrid1.Canvas.Brush.Color := RGB(Random(256), Random(256), Random(256));
StringGrid1.Canvas.FillRect(Rect);

в обработчике событий OnDrawCell работает нормально.

...