Dawno już nie tworzyłem własnych komponentów (od podstaw, rozszerzanie istniejących klas pomijam), a że będę potrzebował ich dość sporo do swojego nowego projektu, dla testów wykonałem sobie prosty komponent dziedziczący z klasy TGraphicControl
i się bawię; Wszystko szło gładko, do wczoraj;
Stworzyłem grupę właściwości, która jest klasą dedykowaną, dziedziczącą z TPersistent
; Instancja tej agregowanej klasy tworzona jest w konstruktorze klasy komponentu oraz zwalniana w jej destruktorze; Poniżej przykład, aby zobrazować jak wygląda użycie takiego cuda:
type
TAppearanceGroup = class(TPersistent)
private
FLineWidth: Integer;
FLineColor: TColor;
public
constructor Create();
published
property LineWidth: Integer read FLineWidth write FLineWidth default 1;
property LineColor: TColor read FLineColor write FLineColor default clMenuHighlight;
end;
type
TGraphicControl1 = class(TGraphicControl)
private
FAppearance: TAppearanceGroup;
private
procedure SetAppearance(AAppearance: TAppearanceGroup);
protected
procedure Paint(); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy(); override;
published
property Appearance: TAppearanceGroup read FAppearance write SetAppearance;
end;
procedure Register;
implementation
constructor TAppearanceGroup.Create();
begin
inherited Create();
FLineWidth := 1;
FLineColor := clMenuHighlight;
end;
constructor TGraphicControl1.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FAppearance := TAppearanceGroup.Create();
end;
destructor TGraphicControl1.Destroy();
begin
FAppearance.Free();
inherited Destroy();
end;
procedure TGraphicControl1.SetAppearance(AAppearance: TAppearanceGroup);
begin
FAppearance.Assign(AAppearance);
Invalidate();
end;
procedure TGraphicControl1.Paint();
begin
with Canvas do
begin
Pen.Color := FAppearance.FLineColor;
Pen.Width := FAppearance.FLineWidth;
Brush.Style := bsClear;
FillRect(Rect(0, 0, Width, Height));
Rectangle(Rect(5, 5, Width - 5, Height - 5));
end;
end;
procedure Register;
begin
RegisterComponents('Tests', [TGraphicControl1]);
end;
Jak widać w powyższym kodzie, klasa komponentu posiada właściwość Appearance
, której dane odczytywane są z pola FAppearance
, a modyfikowane za pomocą metody SetAppearance
; Wszystko gra, nowy węzeł właściwości pokazuje się w OI, a sam komponent wygląda tak, jak mu kazałem wyglądać (według tego, co jest w metodzie Paint
):
Wszystko pięknie ładnie, ale jest jeden problem; Podczas zmiany wartości którejkolwiek właściwości z grupy Appearance
(LineWidth
lub LineColor
), natychmiastowo komponent musi zostać odmalowany; To oczywiste - zmieniam kolor ramki, więc chcę widzieć jak wygląda z innym kolorem (czy z inną szerokością linii);
Z moich badań wynika, że po zmianie wartości właściwości z grupy Appearance
, metoda SetAppearance
nie jest w ogóle wywoływana; W tej metodzie musi być wywołana jakaś metoda, która pozwoli przemalować komponent (np. Invalidate
którego użyłem w powyższym przykładzie, lub chociaż Repaint
); W tym przykładowym kodzie chodzi tylko o przemalowanie, ale w tym nad którym pracuję, po zmianie wartości którejś z agregowanych właściwości, ma być wykonane więcej czynności; Jednak jeśli uda się przemalować komponent, to i nie będzie problemu z wykonaniem innych czynności;
Czy ktoś wie jak i gdzie w takim razie móc zareagować na zmianę wartości właściwości? W jaki sposób móc zareagować od razu, nie czekając na przemalowanie komponentu, wywołane np. przesunięciem komponentu, jego rozciągnięciem czy odsłonięciem przez inne okno?
Wczoraj i dziś dość sporo poczytałem, jednak nie mogę dojść do tego, dlaczego metoda SetAppearance
nie zostaje wywołana - zapomniałem już jak się to robi; Jeśli byłby ktoś w stanie odpowiedzieć w jaki sposób w zareagować na zmianę właściwości umieszczonej w podklasie, czy nawet w jaki sposób w ogóle korzystać z takich klas i grupowania właściwości, to byłbym wdzięczny;
Pamiętam jednak, że właśnie w ten sposób aktualizuje się klasę z zagnieżdżonymi właściwościami (przez osobną metodę i wykonanie Assign
- to znalazłem choćby na stackoverflow), ale jak widać coś jest nie tak, skoro właściwości zostają zapamiętane, ale metoda ustawiająca nie zostaje w ogóle wywołana;
Proszę więc o wskazówki, bo problem jest na pewno banalny, jednak coś źle robię i nie udało mi się znaleźć tego "złego kodu";
PS: Kod testuję pod Delphi7, jakby kto pytał.
- object-inspector-and-form.png (12 KB) - ściągnięć: 148