Jak narysować komponent tak aby wychodził poza formę?

0

Chodzi o coś takiego jak np. ComboBox, który po rozwinięciu wychodzi poza formę, na której został położony.
Jak zrobić by np. po naciśnięciu guzika pojawił się striggrid (to nie problem), który wyjdzie poza formę i będzie poza nią widoczny i tu kłopot.

1

Żaden komponent nie może wystawać poza obszar rodzica (komponentu grupującego lub całego formularza), bo zostanie ucięty. To co wystaje poza formularz to nic innego jak osobne okno w stylu popup. W podobny sposób realizowane jest wyświetlanie menu głównego, menu kontekstowych, tak samo jak wszelkich hintów i temu podobnych baloników.

Tak więc jeśli potrzebujesz zrobić obszar wysuwany poza formularz, to musi on być osobnym oknem.

Wszystko zależy od wymagań, bo można też posłużyć się sztuczkami implementacyjnymi w celu stworzenia jedynie efektu wystawania. Jakiś czas temu pokazałem w jaki sposób zrobiłem formularz z wystającymi panelami. Fizycznie te panele w całości znajdują się w oknie-rodzicu i nie wystają poza jego obszar – po prostu formularzowi ustala się nieregularny kształt.

0

Zgadza się, ta wystająca lista z ComboBox jest osobnym oknem, które dziedziczy w 2 pokoleniu po TWinControl, w związku z tym tak, jak piszesz - jest osobnym oknem jakby "nałożonym" na formatkę, dlatego może wystawać poza jej obrys.

ALE...

TStringGrid to typ, który jest 6 poziomów dziedziczenia pod TWinControl, więc w sumie także jest oknem, a nie jakimś "rysunkowym wytworem" istniejącym tylko na Canvasie swojego rodzica. W związku z tym pytanie - dlaczego jeden potomek TWinControl może wystawać poza swoje okno nadrzędne, natomiast inny już nie? Masz pomysł @furious programming dlaczego tak się dzieje? (Sprawdzałem genealogię komponentów w Lazarusie, ale podejrzewam, że w Delphi struktura dziedziczenia jest podobna).

A teraz coś dla OP - nie wiem dokładnie, co chcesz osiągnąć, ale może rozwiązaniem Twojego problemu będzie stworzenie kolejnego okna z BorderStyle := bsNone o szerokości i wysokości równej rozmiarom stringgrida, w którym osadzisz sobie właśnie tego grida. Po wciśnięciu przycisku, tamto okno będzie się pokazywać w wymagany przez Ciebie sposób, może się przesuwać razem z oknem "głównym", okno główne możesz blokować na czas wyświetlania okna z tabelką albo nadal pozostawić aktywne. Opcji jest wiele, wszystko zależy od Ciebie, w każdym razie - masz pomysł, jak podejść do tematu.

0
cerrato napisał(a):

TStringGrid to typ, który jest 6 poziomów dziedziczenia pod TWinControl, więc w sumie także jest oknem, a nie jakimś "rysunkowym wytworem" istniejącym tylko na Canvasie swojego rodzica.

Tak, wszystko co dziedziczy po TWinControl jest oknem, tyle że nomenklatura w tym przypadku może być myląca. Dla systemu wszystko co posiada własny uchwyt jest oknem, czyli formularze oraz wszystkie komponenty dziedziczące po TWinControl. O tym właśnie informuje prefiks Win – okienko – ale to w kontekście Lazarusa czy Delphi, bo system nie operuje na klasach.

W związku z tym pytanie - dlaczego jeden potomek TWinControl może wystawać poza swoje okno nadrzędne, natomiast inny już nie?

To że dany element dziedziczy z klasy TWinControl nie oznacza automatycznie, że staje się on niezależnym oknem. Zwykłe komponenty muszą być osadzone w innym (kontrolce lub formularzu) – jeśli się tego nie zrobi to komponent w ogóle nie będzie renderowany.

Odpowiedzi należy szukać w klasie TCustomForm, jako bazowej klasie dla wszystkich formularzy. Do przekopania są tysiące linii kodu, więc trzeba będzie się trochę pomęczyć, aby poznać prawdę. ;)

Ewentualnie można się zapoznać z niskopoziomowym tworzeniem okien i ich zawartości za pomocą czystego WinAPI – tam również można znaleźć odpowiedź na to pytanie. W końcu klasy z LCL są jedynie wrapperami na wywołania funkcji systemowych, które wołane są pośrednio za pomocą bieżącego widgetsetu.

0

Wiem, że można przeglądać sobie po kolei źródła i szukać natchnienia ;) Po prostu miałem nadzieję, że Ty jako osoba z pewnym doświadczeniem w tworzeniu komponentów już miałeś z tym wcześniej styczność i będziesz coś wiedział w temacie.

Żeby było śmieszniej - ani grid, ani combobox nie mają po swojej "drodze" do TWinControl nigdzie TCustomForm, więc nie daje to prostego wytłumaczenia. Jakby jeden z tych dwóch komponentów miał customform'a jako rodzica, a drugi nie, to byśmy mieli sprawę jasną. Ale w obecnej postaci to nadal nie wiadomo, z czego ta różnica wynika :(

W końcu LCL jest jedynie wrapperem na wywołania funkcji systemowych

Tylko pamiętajmy, że LCL jest wieloplaformowe, a WinApi to stricte Windows

1

Pamiętaj, że kontrolki typu TComboBox czy TStringGrid do komponenty stricte systemowe, których zawartością, działaniem i renderowaniem w dużej mierze zajmuje się system. Klasa opisująca taką kontrolkę służy głównie do gromadzenia jej ustawień i definicji pewnych metod pozwalających na manipulowanie nią (np. renderowaniem zawartości pola edycyjnego lub pozycji w rozwijalnym menu).

Dlatego np. w klasie TCustomComboBox nie znajdziesz pola z referencją okienka, które jest wysuwane po kliknięciu w przycisk strzałeczki. Referencji do przycisku strzałki też nie znajdziesz w tej klasie ani żadnej innej znajdującej się na drodze dziedziczenia pomiędzy TWinControl a TComboBox.

cerrato napisał(a):

Tylko pamiętajmy, że LCL jest wieloplaformowe, a WinApi to stricte Windows

Dlatego napisałem w dalszej części cytowanego zdania, że robi to pośrednio za pomocą bieżącego widgetsetu. A to który widgetset zostanie utworzony i przypisany do globalnej zmiennej w module Interfaces zależy od wielu ustawień projektu i kompilatora.


Nigdy nie kopałem na tyle głęboko, aby poznać tego typu szczegóły. Ale z drugiej strony, miałem już okazję ręcznie implementować tego typu funkcjonalności, gdzie zachowaniem, wyglądem i zawartością w całości zajmują się klasy własnych komponentów.

Przykładem jest własny mechanizm hintów dla mojej aplikacji. Tam można znaleźć wszystko – referencję okna hinta, timery, metody obsługujące timery i pokazujące/ukrywające okienko, renderujące jego zawartość itd. Customowy interfejs i pisane od podstaw komponenty wykluczają konieczność tworzenia wrapperów na elementy systemowe i pozbawiają systemu kontroli nad nim, ale z drugiej strony taki wybór zmusza do pisania masy kodu zarządzającego tym wszystkim. No ale albo rybki, albo akwarium. ;)

0

Bardzo dziękuję za szczegółowe wyjaśnienie i rzeczową dyskusję.
Na co dzień nie mam zbyt wiele czasu by kopać w komponentach, ale pierwsze co przyszło mi do głowy, to właśnie budowa komponentu na TForm albo na TFrame.
Zbudowałem sobie taki bardziej skomplikowany ComboBox i DBComboBox złożony z kilku kontrolek ale jako podstawy użyłem TPanel i właśnie głównym problemem jest to, że komponent umieszczony u dołu okna zwykle po rozwinięciu bywa za krótki. Spróbuję podjeść do tematu raz jeszcze, tym razem na bazie TCustomForm. Nie mam tylko natenczas jeszcze koncepcji jak przykleić jedną formę do drugiej, tak by można je było razem przesuwać. W ostateczności użyję zwykłe ShowModal, tak by rodzic się nie przesuwał, gdy ComboBox będzie rozwinięty.

0
krzynio napisał(a):

Zbudowałem sobie taki bardziej skomplikowany ComboBox i DBComboBox złożony z kilku kontrolek ale jako podstawy użyłem TPanel i właśnie głównym problemem jest to, że komponent umieszczony u dołu okna zwykle po rozwinięciu bywa za krótki.

Zawsze możesz go wyświetlać w całości w obrębie formularza. I tak musisz walidować jego pozycję, tak aby okno po wysunięciu w całości mieściło się na ekranie (dla stanowiska jednoekranowego) lub na pulpicie (w przypadku stanowiska wieloekranowego).

Spróbuję podjeść do tematu raz jeszcze, tym razem na bazie TCustomForm.

Nie musi to być koniecznie TCustomForm – możesz dziedziczyć bezpośrednio z TForm. Obie te klasy zawierają praktycznie taką samą funkcjonalność, tyle że TForm posiada właściwości przesunięte z sekcji public do published, dzięki czemu m.in. są one widoczne w oknie inspektora obiektów.

Nie mam tylko natenczas jeszcze koncepcji jak przykleić jedną formę do drugiej, tak by można je było razem przesuwać.

Od biedy możesz sobie oprogramować zdarzenie Form.OnChangeBounds i z jego poziomu powiadamiać komponent, aby zaktualizował pozycję wysuniętego okienka. Ale to biedne rozwiązanie, bo zmusza do umieszczenia takiego kodu w klasie formularza, a nie komponentu zarządzającego wysuwanym okienkiem. Musisz zagłębić się w kod komponentu i wybadać czy jakaś metoda zostaje wywołana podczas przesuwania okna.

Przy czym domyślnie ComboBox zwija swoje okienko, jeśli to traci focusa – tak standardowo działają okienka typu popup. Takie rzeczy oprogramowane są najprawdopodobniej w metodzie obsługi komunikatu WM_KILLFOCUS.

0

Może dołożyc trzeba kolejną formę bez belki górnej, o wymiarach wyświetlanego komponentu, ktory ma wyjsc za formę główną programu i na niej umiescic komponent bliźniaczy do tego na formie głwnej, wtedy jak go nie ustawisz to bez problemu pojawi sie tam gdzie nadzasz wspolrzedne tej nowej formy... dodatkowo zyskasz mozliwosc ustawienia alphablend czyli przezroczystosci...

Gdyby efekt nie byl zadowalający mozesz jeszcze kopiowac obraz tła pod formą dodatkową i rysować go na canvasie tej formy. Albo użyć wątku pobocznego do ustalania rozmiaru formy dodatkowej w zaleznosci od stanu komponentu na nim rysowanego przez srodowisko.

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