Obrazek jako tło Form - proporcje

0

Witam.
Próbuję (na razie "wyklikać") coś takiego:

  • na Tform jest Tpicture z właściwością align=alclient i proportional, stretch=true. Po to, żeby obrazek rozciągał się wraz z formą (jako tło), ale zachowywał proporcje.

I obrazek ładnie zmienia swoją wielkość podczas skalowania okna, pozostając we właściwych proporcjach, ale wolną przestrzeń na Form w pionie lub poziomie (zależy jak zmieni się kształt okna) wypełnia pusta przestrzeń. Niby naturalne, skoro chce się zachować proporcje, ale ja chciałbym inaczej. Chciałbym, żeby obrazek wyrównywany był do dłuższego boku okna a drugi wymiar obrazka był obcinany i pojawiał się suwak.

Nie wiem, czy jasno to napisałem, w każdym razie nie wiem jak to ugryźć, choć próbuję na różne sposoby. Czy to, co napisałem jest w ogóle zrozumiałe? Czy nie da się bez ilustracji?

0

Samym TImage tego nie zrobisz, bo ten nie daje takiej możliwości i z racji bycia kontrolką graficzną, nie obsługuje pasków przesuwania. Te natomiast obsługiwane mogą być przez formularz, gdy jego zawartość nie mieści się w obszarze klienta. Aby tak było, trzeba ustawić AutoScroll oraz HorzScrollBar.Visible i/lub VertScrollBar.Visible na True.

Do tego trzeba ręcznie ustalać rozmiar komponentu. Ustaw Align na wartość domyślną i w zdarzeniu OnResize lub metodzie ChangeBounds formularza sam obliczaj rozmiar kontrolki na podstawie rozmiaru obrazka (odczytuj ją z TImage.Picture). Natomiast rozmiar obszaru klienta pobierz z właściwości ClientWidth i ClientHeight – te właściwości zawierają rozmiar obszaru bez scrollbarów.

0
furious programming napisał(a):

Samym TImage tego nie zrobisz, bo ten nie daje takiej możliwości i z racji bycia kontrolką graficzną, nie obsługuje pasków przesuwania. Te natomiast obsługiwane mogą być przez formularz, gdy jego zawartość nie mieści się w obszarze klienta. Aby tak było, trzeba ustawić AutoScroll oraz HorzScrollBar.Visible i/lub VertScrollBar.Visible na True.

Tak, to jasne. BTW: nie mogę edytować posta na którego ktoś już odpisał? (bo rzeczywiście chodzi o TImage a nie TPicture).

Do tego trzeba ręcznie ustalać rozmiar komponentu. Ustaw Align na wartość domyślną i w zdarzeniu OnResize lub metodzie ChangeBounds formularza sam obliczaj rozmiar kontrolki na podstawie rozmiaru obrazka (odczytuj ją z TImage.Picture). Natomiast rozmiar obszaru klienta pobierz z właściwości ClientWidth i ClientHeight – te właściwości zawierają rozmiar obszaru bez scrollbarów.

Tak, masz rację, to do tego zmierza. Myślałem, że da się to skonfigurować. Zacząłem już tak próbować, ale przy zdarzeniu OnPaint...

0

Jeśli bardzo chcesz to możesz skorzystać z OnPaint i malować obrazek za pomocą Canvas.StretchDraw na płótnie okna. Jednak w takim przypadku trzeba będzie ręcznie obliczać proporcje i wymiary obrazu względem obszaru klienta i ręcznie pokazywać, ukrywać i konfigurować paski przewijania.

Rozwiązanie z TImage jest prostsze i wymaga mniejszej ilości kodu do naklepania.

0

Dokładnie tak zacząłem kombinować. Czy mógłbyś mi wyjaśnić, tak bardziej łopatologicznie, w czym by ta Twoja propozycja była lepsza/prostsza? Bo może jest właściwsza a nie kumam różnicy. Bez ręcznego wyliczania proporcji.

0

IMO bez obliczania proporcji się nie obejdzie, skoro potrzebujesz pokazywać albo dolny pasek przewijania, albo boczny. Ale nie ma się co martwić – wyznaczenie proporcji to podzielenie szerokości obrazka przez jego wysokość – i tyle.

0

Tu nie chodzi o matematykę ;) Dlaczego napisałeś, że ręcznie trzeba będzie pokazywać/ukrywać paski przewijania? Auto-scroll nie zadziała?

0
darekdarek napisał(a):

Dlaczego napisałeś, że ręcznie trzeba będzie pokazywać/ukrywać paski przewijania? Auto-scroll nie zadziała?

Jeśli chcesz samemu malować obraz na płótnie formularza, to samemu musisz te paski pokazywać. W końcu one pojawiają się wtedy, kiedy zawartość się nie mieści (a tą zawartością są komponenty).

0

Próbujesz mi powiedzieć, że Canvas formsa nie będzie nigdy miało rozmiarów z zapasem (większych niż form)?

0

No oczywiście – a niby dlaczego miałby być większy?

0

Nie twierdzę, że ma być. Rozumiem. Tylko to komplikuje całą zabawę (konkretnie z metodą Canvas.StretchDraw). Dzięki. Męczę się z tym, dobrze, że już przynajmniej wiem, że raczej tego nie wyklikam na obiektach.

Bo kombinowałem jakoś jeszcze tak: żeby na TForm położyć TPanel, który ma Canvas i może "wystawać" poza TForm (a na to Panel dopiero Image). I tu chciałem to skonfigurować właściwościami w ObjectInspectorze. No i poległem, póki co.

0
darekdarek napisał(a):

Męczę się z tym, dobrze, że już przynajmniej wiem, że raczej tego nie wyklikam na obiektach.

Właśnie w tym rzecz, że jeśli chcesz skorzystać z automatycznego dostosowywania pasków przewijania, to musisz mieć komponent (bo tylko wtedy te paski będą pokazywane przy AutoScroll).

Bo kombinowałem jakoś jeszcze tak: żeby na TForm położyć TPanel, który ma Canvas i może "wystawać" poza TForm (a na to Panel dopiero Image).

To samo możesz uzyskać ze zwykłym TImage i włączonym AutoScroll i to przecież Ci cały czas sugeruję. Jedyne co musisz zrobić to dostosować rozmiar komponentu podczas rozciągania formularza w taki sposób, aby komponent z obrazkiem nie mieścił się w obszarze klienta, aby formularz mógł pokazać/ukryć scrollbar(y).

0

To i tak nie działa, nie wiem czemu.
Formuła typu image1.width:=form1.width; nie robi nic ani wewnątrz OnResize, ani wewn. ChangeBounds. Tak jakby rozmiary okna były jeszcze niedostępne w momencie ich wywoływania (tylko chwilę potem, już po narysowaniu wszystkiego) Nie wiem...

0
darekdarek napisał(a):

Formuła typu image1.width:=form1.width; nie robi nic ani wewnątrz OnResize […]

Pisałem o ClientWidth i ClientHeight. Poza tym to działa:

procedure TMainForm.FormResize(Sender: TObject);
begin
  Image.Width := ClientWidth;
  Image.Height := ClientHeight;
end;

U mnie śmiga bez problemu.

[…] ani wewn. ChangeBounds. Tak jakby rozmiary okna były jeszcze niedostępne w momencie ich wywoływania (tylko chwilę potem, już po narysowaniu wszystkiego) Nie wiem...

Nowe wymiary okna są dostępne zarówno w zdarzeniu OnResize, jak i w metodzie ChangeBounds. W przypadku tego drugiego, jeśli już nadpisujemy tę metodę, to bieżące wymiary są dostępne przed wywołaniem wersji bazowej (za pomocą inherited), a nowe tuż po tym wywołaniu:

procedure TMainForm.ChangeBounds(ALeft, ATop, AWidth, AHeight: Integer; AKeepBase: Boolean);
begin
  // tu jeszcze masz dostęp do starych rozmiarów

  inherited ChangeBounds(ALeft, ATop, AWidth, AHeight, AKeepBase);

  // a tutaj już do nowych
end;

Problem jednak jest taki, że ta metoda może być wywoływana wielokrotnie zanim obiekt komponentu zostanie utworzony w pamięci, dlatego trzeba to najpierw sprawdzić, zanim zacznie się korzystać z referencji kontrolki.

Drugi problem jest taki, że komponent z obrazkiem nie zostanie dopasowany do rozmiarów okna po pokazaniu okna na ekranie. Formularz się otworzy, ale kontrolka nadal będzie posiadać domyślne wymiary (te z designera) i dopiero wtedy podczas rozciągania okna zacznie ona się zachowywać tak jak chcemy (czyli dopasowywać swój rozmiar do klienta).

Wszystko dlatego, że metoda ChangeBounds okna jest wywoływana podczas ładowania danych formularza (wtedy jeszcze Image nie istnieje), ale nie jest wołana podczas wczytywania danych komponentu Image. Aby to zabezpieczyć, wystarczy nadpisać jeszcze jedną metodę – Loaded – która wywoływana jest tuż po załadowaniu i skonfigurowaniu formularza, i w niej wywołać ChangeBounds, przekazując bieżące wymiary.

Cały kod niżej, a w załączniku masz projekt do przetestowania lokalnie:

type
  TMainForm = class(TForm)
    Image: TImage;
  protected
    procedure Loaded(); override;
    procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: Integer; AKeepBase: Boolean); override;
  end;

{..}

procedure TMainForm.Loaded();
begin
  inherited Loaded();
  ChangeBounds(Left, Top, Width, Height, True);
end;

procedure TMainForm.ChangeBounds(ALeft, ATop, AWidth, AHeight: Integer; AKeepBase: Boolean);
begin
  inherited ChangeBounds(ALeft, ATop, AWidth, AHeight, AKeepBase);

  if Assigned(Image) then
    Image.BoundsRect := Self.ClientRect;
end;
0

Dzięki za trud. Odpiszę jak przeanalizuję i przetestuję.

0

To co pokazałem wyżej sprawia, że rozmiar komponentu będzie zgodny z rozmiarem obszaru klienta. Podałem ten przykład dlatego, że sam nie poradziłeś sobie z obsługą OnResize i ChangeBounds.

Jak opanujesz działanie tych przykładów, to powinieneś się zabrać najpierw za wymyślenie sposobu dopasowywania według proporcji i dopiero wtedy za pisanie kodu. Przy czym łatwiejsze będzie wykorzystanie OnResize.

0
furious programming napisał(a):

Podałem ten przykład dlatego, że sam nie poradziłeś sobie z obsługą OnResize i ChangeBounds.

Z ChangeBounds nie poradziłbym sobie, bo poległbym na inherited. Nie znalazłbym tego problemu.
Ale czemu OnResize u mnie po prostu nie działa, to nie wiem.

Inna sprawa - czy te dwa przykłady działają w zasadzie identycznie? Czy OnResize zadziała dopiero po puszczeniu LPM? Pytam, bo nie działa mi ani tak, ani tak.

Jak opanujesz działanie tych przykładów, to powinieneś się zabrać najpierw za wymyślenie sposobu dopasowywania według proporcji i dopiero wtedy za pisanie kodu.

Tak, po kolei.

0
darekdarek napisał(a):

Z ChangeBounds nie poradziłbym sobie, bo poległbym na inherited. Nie znalazłbym tego problemu.

inherited to nie jest problem – to podstawowa własność programowania obiektowego. Musisz się podszkolić z obiektówki, jeśli chcesz sprawnie tworzyć aplikacje okienkowe.

Ale czemu OnResize u mnie po prostu nie działa, to nie wiem.

No ja też nie wiem, bo nie widziałem Twojego kodu.

Inna sprawa - czy te dwa przykłady działają w zasadzie identycznie?

Oba przykłady działają identycznie, zmieniając rozmiar komponentu w trakcie rozciągania okna – na żywo, nie po puszczeniu LPM. Dodaj do załączników projekt który Ci nie działa to go sprawdzę i napiszę co jest w nim nie tak.

We wcześniejszym poście dodałem projekt do załączników – pobierz sobie i przetestuj.

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