Почему обновление пользовательских компонентов при изменении свойств?
Я создал компонент TGridPaintBox, основанный на TPaintBox. Это, в основном, paintbox с добавленной "функциональностью сетки". Это не сетка данных. Больше похоже на компонент шахматной доски.
В проводнике объектов я могу установить определенные свойства. Самое главное, что я могу установить размеры сетки (количество ячеек в/в), а также параметры, относящиеся к рисованию. Являются ли ячейки квадратными, цвет четных/четных клеток и т.д.
Моя первая версия этого компонента имела свойства непосредственно в классе, и когда я изменил свойство, графический чертеж был немедленно обновлен. По мере того, как компонент рос, я хотел немного улучшить свои свойства и ввел некоторые "свойства параметров", такие как параметры рисования, параметры поведения и т.д. После ознакомления с этим рисунок времени разработки больше не обновляется, как раньше. После изменения свойства я должен щелкнуть по компоненту для его обновления. Может ли кто-нибудь сказать мне, почему это происходит?
Вот урезанная версия кода. Я надеюсь, что это объяснит поведение:
(PS: Это мой первый компонент, хотя я использую Delphi с 1997 года, поэтому, если кто-нибудь может заметить что-то глупое в том, как я это сделал, пожалуйста, не стесняйтесь говорить мне)
unit GridPaintBox;
interface
type
TGridDrawOption = (gdoSquareCells,gdoCenterCells,gdoDrawCellEdges,gdoDrawFocus);
TGridDrawOptions = set of TGridDrawOption;
TGridOptions = class(TPersistent)
private
FCellsX : integer;
FCellsY : integer;
FDrawOptions : TGridDrawOptions;
public
constructor Create(aGridPaintBox : TGridPaintBox);
procedure Assign(Source : TPersistent); override;
published
property CellsX : integer read FCellsX write FCellsX;
property CellsY : integer read FCellsY write FCellsY;
property DrawOptions : TGridDrawOptions read FDrawOptions write FDrawOptions;
end;
TGridPaintBox = class(TPaintBox)
private
FGridOptions : TGridOptions;
FFocusedX,
FFocusedY : integer;
FOnFocusChanged: TNotifyEvent;
procedure CalculateSizeAndPosition;
procedure DrawCell(X,Y : integer);
procedure DrawCells;
procedure SetGridOptions(const Value: TGridOptions);
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure Paint; override;
procedure SetFocus(X,Y : integer);
published
property OnFocusChanged : TNotifyEvent read FOnFocusChanged write FOnFocusChanged;
property Options : TGridOptions read FGridOptions write SetGridOptions;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TGridPaintBox]);
end;
procedure TGridPaintBox.CalculateSizeAndPosition;
begin
<...>
end;
constructor TGridPaintBox.Create(aOwner: TComponent);
begin
inherited;
FGridOptions := TGridOptions.Create(self);
end;
procedure TGridPaintBox.DrawCell(X, Y: integer);
begin
<...>
end;
procedure TGridPaintBox.DrawCells;
var
X,Y : integer;
begin
CalculateSizeAndPosition;
for Y := 0 to FGridOptions.CellsY-1 do
for X := 0 to FGridOptions.CellsX-1 do
DrawCell(X,Y);
end;
procedure TGridPaintBox.Paint;
begin
Canvas.Font := Font;
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(Rect(0,0,Width,Height));
DrawCells;
if Assigned(OnPaint) then
OnPaint(Self);
end;
procedure TGridPaintBox.SetGridOptions(const Value: TGridOptions);
begin
FGridOptions.Assign(Value);
invalidate;
end;
procedure TGridPaintBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
SetFocus(PixelToCellX(X),PixelToCellY(Y));
inherited;
end;
procedure TGridPaintBox.SetFocus(X, Y: integer);
begin
if (FocusedX=X) and (FocusedY=Y) then
exit;
FFocusedX := X;
FFocusedY := Y;
if assigned(OnFocusChanged) then
OnFocusChanged(self);
invalidate;
end;
constructor TGridOptions.Create(aGridPaintBox : TGridPaintBox);
begin
FCellsX := 20;
FCellsY := 8;
FDrawOptions := [gdoSquareCells,gdoCenterCells,gdoDrawCellEdges];
end;
procedure TGridOptions.Assign(Source : TPersistent);
begin
if Source is TGridOptions then
begin
FCellsX := TGridOptions(Source).CellsX;
FCellsY := TGridOptions(Source).CellsY;
FDrawOptions := TGridOptions(Source).DrawOptions;
end
else
inherited;
end;
end.
Ответы
Ответ 1
Это происходит потому, что у вас нет setter для набора опций, который приведет к аннулированию вашего элемента управления, который принадлежит. Щелчок в дизайнере формы вызывает элемент управления, чтобы он недействителен, но вы должны справиться с этим сами по себе в таком наборе параметров. Поэтому я бы сохранил владельца параметров для лучшего доступа к экземпляру класса прямого владельца и в настройке параметров заставил этого владельца, элемент управления перерисовать:
type
TGridPaintBox = class;
TGridDrawOption = (gdoSquareCells, gdoCenterCells, gdoDrawCellEdges, gdoDrawFocus);
TGridDrawOptions = set of TGridDrawOption;
TGridOptions = class(TPersistent)
private
FOwner: TGridPaintBox;
FCellsX: Integer;
FCellsY: Integer;
FDrawOptions: TGridDrawOptions;
procedure SetCellsX(AValue: Integer);
procedure SetCellsY(AValue: Integer);
procedure SetDrawOptions(const AValue: TGridDrawOptions);
public
constructor Create(AOwner: TGridPaintBox);
procedure Assign(ASource: TPersistent); override;
published
property CellsX: Integer read FCellsX write SetCellsX;
property CellsY: Integer read FCellsY write SetCellsY;
property DrawOptions: TGridDrawOptions read FDrawOptions write SetDrawOptions;
end;
implementation
constructor TGridOptions.Create(AOwner: TGridPaintBox);
begin
FOwner := AOwner;
FCellsX := 20;
FCellsY := 8;
FDrawOptions := [gdoSquareCells, gdoCenterCells, gdoDrawCellEdges];
end;
procedure TGridOptions.SetCellsX(AValue: Integer);
begin
if FCellsX <> AValue then
begin
FCellsX := AValue;
FOwner.Invalidate;
end;
end;
procedure TGridOptions.SetCellsY(AValue: Integer);
begin
if FCellsY <> AValue then
begin
FCellsY := AValue;
FOwner.Invalidate;
end;
end;
procedure TGridOptions.SetDrawOptions(const AValue: TGridDrawOptions);
begin
if FDrawOptions <> AValue then
begin
FDrawOptions := AValue;
FOwner.Invalidate;
end;
end;
Дополнительные примечания:
Если вам явно не нужно иметь опубликованные свойства и события с краской, например, событие Color
, Font
или OnPaint
, выведите свой элемент управления из TGraphicControl
вместо TPaintBox
. Вы можете выбрать, какие свойства и события вы опубликуете самостоятельно.