Dynamiczne tworzenie komponentu: Problem ze skalowaniem

0

Witam,
Próbuję dostosować mój program (który pobiera podstawowe informacje o systemie) do działania w systemie z niestandardowym skalowaniem (testuje na 150%).
Program wyświetla pobrane informacje na statycznych kontrolkach i dynamicznie tworzonych kontrolkach. Program działa OK przy standardowym skalowaniu (100%).
Cuda dzieją się przy niestandardowym skalowaniu - cały program działa OK (właściwość formy Scaled = True), ale nieprawidłowo wyświetlane/skalowane są elementy, które tworzę dynamicznie, w czasie działania programu.

Wszystkie dynamicznie utworzone kontrolki są za duże.
Na formie mam położoną kontrolkę TPageControl. W czasie działania programu dynamicznie tworzę zakładki (TTabSheet), a na nich kontrolki (TLabel).
Coś takiego:

// Create Tabs
iTab := 0;
for i:=0 to IlePotrzebnychZakladek-1 do
	begin
		iTab:=iTab+1;	
		
		TabSheet := TTabSheet.Create(PC_VideoController); // <---PC_VideoController (TPageControl) istnieje
        TabSheet.Caption := 'blabla';
        TabSheet.PageControl := PC_VideoController;
        TabSheet.PageIndex:= iTab-1;
        TabSheet.Name:='TS_VideoController_' + inttostr(iTab);

		// Create labels on each Tab
		with TLabel.Create(TabSheet) do
			begin
				// oryginalne rozmiary kontrolki pobieram z statycznej kontrolki TLabel (prawidłowej)
				Height:=Lbl_OS_Name_Desc.Height;
				Font.Size:= Lbl_OS_Name_Desc.Font.Size;
				Font.Style:=Lbl_OS_Name_Desc.Font.Style;
				Font.Name := Lbl_OS_Name_Desc.Font.Name;
				Font.Charset := Lbl_OS_Name_Desc.Font.Charset;
				Font.Color:=clBlack;

                                // zmienna j okresla ilosc kontrolek na jednej zakladce, ale to nie jest tutaj istotne
				width:=200;
				Left:=20;
				aTop:=10 + (j-1)*Height + (j-1)*iSPace; // czary mary (odleglosc miedzy kontrolkami)
				Top:= aTop;
				Parent:=TabSheet;
				Name:='Lbl_VideoController_' + inttostr(iTab) + '_Desc_' + IntToStr(j);
				Caption:=L_VideoController[j+2] + ':';
				AutoSize:=True;
				Transparent:=True;
				WordWrap:=False;
				Alignment:=taLeftJustify;
				Anchors:=[akLeft, akTop];
				Enabled:=True;
				ParentFont:=False;
				ParentBiDiMode:=False;
			end;
	end;

Zobaczcie - to jest to co się wyświetla:

bad.png

a tak wyglądać powinna:

good.png

Co może być powodem tego zachowania? Co mogę zrobić?
Proszę o wskazówki!

0

A próbowałeś ustawić właściwość Font dla TabSheet?

0

Tak,
Na przykład;

TabSheet.Font.Height:= PC_SystemInfo.Font.Height;
TabSheet.Font.PixelsPerInch:= PC_SystemInfo.Font.PixelsPerInch;
0

A co masz ustawione w oknie Inspektora Obiektów we właściwości Font kontrolki PC_SystemInfo? Spróbowałbym ustawić tej kontrolce ParentFont na False i ogólnie poodpinać wszystko, co może powodować automatyczne dostosowywanie rozmiaru fontu do bieżącego DPI.

0

Nic szczególnego :)
Domyślne wartości.
Wychodzi na to, jakby dynamicznie utworzone kontrolki nie trzymały opcji skalowania tych tworzonych z formularzem...
Nie mam pojęcia co muszę ustawić, żeby to zmienić. Może muszę "ręcznie" przeliczać rozmiar kontrolek... Musiałbym poszukać wzoru (w zależności od DPI).
-Pawel

0

A tak z ciekawości - jak ustawisz Scaled formularza na False to zawartość okna odpowiada tej z designera?

0

Ustawiając opcję Scaled na False kontrolki tworzone statycznie jak i dynamicznie wyglądają tak samo.
Aplikacja nie obsługuje jednak w tym przypadku innego DPI. To mnie nie interesuje.

0

@Pepe niestety muszę Cię chyba zmartwić. Właściwość Scaled będzie działać tylko dla komponentów umieszczonych na formie w designerze. Resztę musisz załatwić sam. Ale przecież nie jest to trudne. Musisz przemnożyć szerokości, wysokości oraz współrzędne o współczynnik:
runtime_DPI/designtime_DPI

0

Na to chyba wygląda.
Zakładając, że muszę właściwości każdej kontrolki przeliczyć sam...
Proszę mnie poprawić, czy dobrze mówię. Domyślnie, Delphi używa w designerze DPI=96 PPI.

  • Jak pobrać bieżące DPI systemu, czy tak? -> Screen.PixelsPerInch;
  • Jak przeliczyć właściwości, np. szerokość: -> RunTime_Label.Width := DesignTime_Label.Width*(RunTime_DPI/DesignTime_DPI);

Czyli by było:

DesignTime_DPI := 96;
RunTime_DPI := Screen.PixelsPerInch;
RunTime_Label.Width := DesignTime_Label.Width*(RunTime_DPI/DesignTime_DPI);

Jeśli ktoś ma doświadczenie w tym temacie, proszę o komentarz :)
-Pawel

1

spróbuj użyć ScaleBy

Rescale control and its children.

ScaleBy resizes a control without moving its upper left corner. This is similar to changing the Height and Width properties, but the control also attempts to rescale and rearrange any child controls to maintain their relative size and placement.

The M and D parameters define a multiplier and divisor by which to scale the control. For example, to make a control 75% of its original size, specify the value of M as 75, and the value of D as 100. Any pair of values that has the same ratio has the same effect. Thus M = 3 and D = 4 also makes the control 75% of its previous size.

To rescale the control's children without rescaling the control itself, use ScaleControls.

Teoretycznie jak użyjesz to na TabSheet to załatwi sprawę

0

No niestety, w teorii powinno działać. Ale nie działa.
Jeden wielki bałagan z tym skalowaniem :)

Mam nadzieję, że ktoś z Was mierzył się z tym problemem i pomoże...

Ps: Szczęśliwego Nowego Roku!

0

@Pepe tak, ja coś takiego robiłem (jednak używam C++ Buildera, ale nie ma to znaczenia za wielkiego).

Pepe napisał(a):

Proszę mnie poprawić, czy dobrze mówię. Domyślnie, Delphi używa w designerze DPI=96 PPI.

  • Jak pobrać bieżące DPI systemu, czy tak? -> Screen.PixelsPerInch;
  • Jak przeliczyć właściwości, np. szerokość: -> RunTime_Label.Width := DesignTime_Label.Width*(RunTime_DPI/DesignTime_DPI);

Tak, z tym, że o ile dobrze kojarzę aby to działało musisz w manifeście zadeklarować, że aplikacja jest 'DPI Awareness' Po więcej odsyłam tu https://msdn.microsoft.com/en-us/library/dn469266(v=vs.85).aspx

Pepe napisał(a):

Czyli by było:

DesignTime_DPI := 96;
RunTime_DPI := Screen.PixelsPerInch;
RunTime_Label.Width := DesignTime_Label.Width*(RunTime_DPI/DesignTime_DPI);

Jeśli ktoś ma doświadczenie w tym temacie, proszę o komentarz :)
-Pawel

Te 96, nie jest stałe. Jeśli u Ciebie na komputerze podczas pisania programu będziesz miał większe (120) DPI to właściwość Form.PixelsPerInch też będzie 120. Nie mniej jednak ja tak robiłem i działało.

0

Moja aplikacja posiada odpowiedni manifest wygenerowany przez Delphi. Jak wiadomo współczesne wersji Delphi (10.1) posiada odpowiednie ustawienia w opcjach projektu, co umożliwia wygenerowanie manifestu automatycznie.

Testowałem te ustawienia (przeliczanie DPI) i niestety nie działa to poprawnie (albo źle to robię). Stosując taki przelicznik na pewno widzę różnicę - czcionki są mniejsze - ale nie takie jakie powinny być. Sama metoda działa ale chyba jest problem z przeliczaniem - być może wynika z zaokrąglania. PixelsPerInch to Integer (nasze działanie daje nam wynik zmiennoprzecinkowy, który zaokrąglam... tu chyba jest problem. Nie jestem pewien.

96 to tylko przykład. Wiadomo, że to jest wartość używana na komputerze, na którym kompilowany jest program.
Mówisz, że stosowałeś to i działało. Mógłbyś poświęcić chwilę i wyciąć z swojego kodu fragment, który pokazuje jak to zrobiłeś?
-Pawel

0

Tak tu trzeba pamiętać, że dzielenie inta przez inta daje int. Ja zapamiętywałem to jako liczba zmiennoprzecinkowa, następnie mnożyłem top, left, width, height przez ten współczynnik i zaokrąglałem do inta.

Kodu nie za bardzo teraz mogę dać, bo to kod pisany w pracy i pisany w C++. Ale na jutro mogę coś naskrobać w Delphi.

0

Byłoby miło. Nie chodzi o jakiś konkretny kod, tylko parę linijek, które prezentują metodę. Może źle zaokrąglam... kto wie :P

0

Dobra zrobiłem szersze testy i okazuje się, że jednak nie działa to do końca tak jak powinno. Nie wiem czemu, ale chyba Delphi jakoś inaczej zaokrągla gdy skaluje kontrolki położone w designerze. Kontrolki tworzone podczas działania programu nie mają dokładnie tych samych współrzędnych co generowane przez Delphi. Ogólnie źle to wygląda. Swoje systemy testowałem na DPI klienta (200%) i tu o żadnych zaokrągleniach nie było mowy, więc wyglądało to dobrze. Po zmianie DPI na nie takie okrągłe widać nierówności.

Nie wiem, ale chyba zostaje wyłączenie Scaled dla formatki i ręczne przeliczanie wszystkich kontrolek w danym oknie. Wtedy powinno być wszystko tak samo.

0

Nie wiem czemu, ale chyba Delphi jakoś inaczej zaokrągla gdy skaluje kontrolki położone w designerze. Kontrolki tworzone podczas działania programu nie mają dokładnie tych samych współrzędnych co generowane przez Delphi.

Jeżeli macie źródła VCL to sprawdźcie sobie jak wygląda kod odpowiedzialny za skalowanie; Ja nie używam Delphi, więc w tej materii nie pomogę.

0

No ogólnie bida z nędzą :(

0

Niestety. Rozmawiałem z kumplem który wymiata bardziej w VCL'u i twierdzi, że zazwyczaj łączenie dynamicznie oraz automatycznie generowanych kontrolek sprawia problemy podczas skalowania na wysokich DPI.

1 użytkowników online, w tym zalogowanych: 0, gości: 1