Użerania się z komponentem ciąg dalszy... Tym razem problem jest bardziej zawiły i dotyczy póki co Delphi7;
Dotyczy on dziedziczonej właściwości Anchors
i dostosowywania swojego rozmiaru w przedefiniowanej metodzie SetBounds
, gdy zbiór ten zawiera poniższe wartości:
Anchors = [akBottom, akLeft, akRight];
Podczas każdorazowej zmiany rozmiaru sprawdzane jest, czy jego szerokość uległa zmianie; Jeżeli tak - odpowiednia metoda "mierzy" zawartość i zwraca nową wysokość; Jeżeli nowa wysokość różni się od poprzedniej, modyfikowana jest zawartość parametru ATop
metody SetBounds
;
Jeżeli dobrze liczę, to różnicę poprzedniej i nowej wysokości trzeba dodać do poprzedniej wysokości; Czyli:
ATop := Self.Top + (ATop - Self.Top);
I to by załatwiało sprawę, ale tylko teoretycznie... Jeżeli uruchomię program i porozciągam formularz jedynie w pionie, to wszystko gra; Rozciąganie tylko w poziomie działa dobrze, ale głupieje randomowo i nie mogę dojść do tego, dlaczego nie wiadomo skąd Top
komponentu zostaje zmieniony na zupełnie inny, niż to wynika z obliczeń; Ba, zostaje randomowo zmieniony nawet wtedy, gdy szerokość komponentu się nie zmienia, czyli obliczenia nowej wysokości i odstępu od góry są pomijane;
Wykonałem testowy komponent, implementując jedynie to co nie działa prawidłowo, czyli metodę SetBounds
i właściwość Anchors
; Kod komponentu poniżej:
type
TTestGraphControl = class(TGraphicControl)
protected
procedure Paint(); override;
public
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
published
property Anchors;
end;
{...}
// metodę Paint pomijam, bo nie dotyczy problemu
procedure TTestGraphControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
{ symulacja metody obliczającej nową wysokość komponentu }
function SetNewHeigh(): Integer;
begin
if Abs(Self.Width - AWidth) > 25 then
Result := Self.Height + (Self.Width - AWidth)
else
Result := AHeight;
end;
begin
{ jeżeli szerokość komponentu się zmieniła }
if Self.Width <> AWidth then
begin
{ jeżeli w zbiorze Anchors nie znajdują się razem flagi akTop i akBottom }
if (akBottom in Anchors) and not (akTop in Anchors) then
begin
{ symulacja metody obliczającej wysokość komponentu na podstawie jego zawartości }
AHeight := SetNewHeigh();
{ jeśli wysokość komponentu się zmieniła - zwiększamy odpowiednio odstęp od góry,
obliczając różnicę starej i nowej wysokości i dodając ją do obecnego odstępu }
if Self.Height <> AHeight then
ATop := Self.Top + (Self.Height - AHeight);
end;
end;
{ wywołanie bazowej metody, podając zmodyfikowane parametry }
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
end;
Jak widać w powyższym kodzie, nowa wysokość komponentu i jego odstęp od góry obliczane są jedynie w przypadku, gdy zmieniła się szerokość komponentu, oraz gdy Anchors
zawiera flagę akBottom
i jednocześnie nie zawiera flagi akTop
; Niestety ta metoda coś wariuje, źle i w złych momentach oblicza odstęp od góry; Niestety nie mogę namierzyć kodu, który to powoduje; To obliczanie nowej wysokości to tylko symulacja - wysokość komponentu nie zawsze zmienia się, jeżeli zmieniła się jego szerokość, stąd ten przykładowy warunek;
Objawy poniżej:
Co ciekawe, jeżeli szerokość komponentu nie zmieniła się, czyli wszystkie obliczenia i modyfikacje parametrów metody SetBounds
są pomijane, komponent i tak zmienia swój offset od góry (wartość ATop
); Widać to zaraz po uruchomieniu programu, gdy rozciągam formularz jedynie w pionie; Obliczenia są pomijane, więc dlaczego komponent zmienia swoje położenie?
No właśnie - sprawdziłem kod klasy TControl
; W bazowej metodzie SetBounds
wywołane zostają metody UpdateAnchorRules
, RequestAlign
i Resize
, które najwyraźniej coś jeszcze działają na rozmiarach i położeniu komponentu, psując moje obliczenia; Niestety nie mogę pominąć wywołania inherited SetBounds
, dlatego że inaczej nie zmienię rozmiarów komponentu, a nawet jeśli zmieniałbym bezpośrednio właściwości Left
, Top
, Width
czy Height
komponentu, to stworzę bombę rekurencyjną powodującą StackOverflow
; I nic dziwnego, bo zmiana wartości którejkolwiek z wymienionych właściwości wywołuje SetBounds
i kicha;
Czy ktoś ogarnia to w jaki sposób poprawnie ustawić położenie i rozmiar komponentu, bądź oszukać wewnętrzne SetBounds
? Pałuję się z tym już drugi dzień i pomysły mi się skończyły... Najwidoczniej coś pominąłem, bo obliczenia wydają mi się poprawne;
Do posta dołączam źródła komponentu oraz źródła aplikacji, która korzysta z tego komponentu; Aby odtworzyć buga, wystarczy się chwilę pobawić formularzem, rozciągając go w różnych kierunkach; W końcu odstęp dołu komponentu od dołu formularza będzie zupełnie inny, niż zaraz po uruchomieniu programu;
Pamiętajcie, że chodzi o Delphi7; Dziękuję z góry za pomoc.