Przejdź do treści

Tworzenie form

Forma - to definicja okna, które może być wyświetlone przez klienta. Definicja formy składa się z komponentów umieszczonych jeden wewnątrz drugiego w wyniku czego forma posiada strukturę drzewiastą. Komponentami mogą być pojedyncze kontrolki lub kontenery zawierające wewnątrz grupę kontrolek. Komponenty mogą także mieć przypisane metody sterujące widocznością lub wyglądem tego komponentu w zależności od redagowanych lub wyświetlanych danych. Każda forma jest definiowana w ramach jakiegoś obiektu.

Rodzaje form

Podstawowe zastosowanie form to tworzenie interfejsów użytkownika, dzięki którym użytkownicy mogą pracować z oprogramowaniem. Ze względu na zastosowanie, formy dzielimy na:

  • forma do przeglądania danych (domyślny symbol: BROWSE) - zwykle zawiera tabelę z danymi, przyciski z akcjami, parametry służące do filtracji danych w tabeli
  • forma do edycji danych (domyślny symbol: EDIT) - zwykle zawiera szereg pól pogrupowanych w panele lub zakładki, czasem dodatkowe przyciski z akcjami
  • forma do wyboru wartości ze słownika (domyślny symbol: DICT) - zwykle zawiera tabelę z danymi i przycisk do wybrana wartości
  • forma do zakładania nowego rekordu (domyślny symbol: NEW) - zwykle wygląda jak uproszczona forma EDIT. Jeśli forma o symbolu NEW nie istnieje, to do zakładania nowego rekordu używana jest ta sama forma, która służy do edycji danych

Więcej na temat dostępnych rodzajów form i zasad ich użycia przeczytasz w tym artykule.

Powiązane artykuły

Nowa forma

Aby założyć nową formę ustaw się w oknie struktury projektu na obiekcie, w ramach którego chcesz dodać formę i wybierz funkcję: Nowy -> Inne okno. Wypełnij dane podstawowe (co najmniej symbol formy) i naciśnij Zapisz. Nowa forma pojawi się w drzewie struktury projektu. Kliknij na niej, a pojawi się panel z definicją struktury formy. W centralnej części okna zobaczysz drzewiastą strukturę nowo utworzonej formy, a po lewej stronie zwiniętą listę elementów, które możesz umieścić na formie, a także osobną listę innych form, które możesz zagnieździć lub umieścić w postaci linka. Na liście elementów możesz znaleźć:

  • komponenty kontenerowe do umieszczania w nich innych komponentów (panel z zakładkami, panel poziomy, panel pionowy)
  • komponenty do zbiorczej prezentacji danych (tabela, wykres, kostka, itp.)
  • kontrolki pól danych
  • kontrolki parametrów
  • akcje - umieszczane w postaci przycisku lub linku

Wybierz z listy po lewej stronie element, który chcesz umieścić na formie i przeciągnij go poprzez drag&drop na strukturę formy. W ten sposób umieszczasz dany element na formie. Przeciągając w odpowiedni sposób możesz umieścić element pod innym elementem na formie (niebieska strzałka) albo wewnątrz niego (żółta strzałka). Możesz to także zrobić wywołując funkcję Dodaj element - zwłaszcza jeśli forma jest pusta, gdyż wtedy drag&drop nie działa. Po prawej stronie widzisz atrybuty elementu, na którym stoisz - takie jak etykieta, ikona, rozmiar i inne. Możesz je zmienić. Do niektórych wizualnych atrybutów elementu możesz przypiąć metody, dzięki czemu wygląd formy będzie się zmieniał w trakcie jej życia. Projektując wygląd formy nie masz możliwości precyzyjnego rozmieszczania elementów co do piksela. Określasz jedynie rozmiar pojedynczych elementów w jednostkach logicznych, ich kolejność oraz to, wewnątrz jakiego elementu kontenerowego się znajdują. Na podstawie tych informacji forma jest budowana po stronie klienta i dopiero klient rozmieszcza fizycznie elementy formy. Ma to wiele zalet, np. taką, że jeśli dowolny element zostanie ukryty, to sąsiednie elementy zostaną automatycznie przesunięte, aby nie pozostało puste miejsce. Umieszczając na formie komponent do zbiorczej prezentacji danych (tabela, wykres, kostka OLAP) zobaczysz w drzewie struktury formy jego podelementy pozwalające na szybką i łatwą konfigurację. Na przykład umieszczając na formie tabelę możesz przeciągnąć pola danych jako jej kolumny oraz akcje jako przyciski widoczne bezpośrednio ponad tabelą. Jeśli z kolei umieścisz wykres, możesz z łatwością przeciągnąć pole kategorii oraz pola z których budowane są serie danych a także pola grupujące.

Szybkie zakładanie formy

Wybierając funkcję: Nowy -> Okno przeglądania lub Nowy -> Okno edycji możesz szybko założyć standardowe okna przeglądania lub edycji danych. Musisz jedynie wpisać tytuł okna i wybrać pola które mają się znaleźć w oknie.

Rozmieszczanie komponentów na formie

Aby pola w formie wyglądały ładnie i wyświetlały się poprawnie, należy je umieszczać w kontenerach, takich jak: left-right panel, top-down panel, page control. Domyślnie w kontenerach top-down panel i page control pola układają się od góry do dołu, a w kontenerze left-right panel - od lewej do prawej . Pola domyślnie mają umiarkowane rozmiary - zwykle 5 jednostek logicznych. Można je zwiększyć lub zmniejszyć w miarę potrzeby.

Jednak czasami na formie umieszczasz pola, które powinny zająć możliwie największą przestrzeń formy - zwłaszcza pola MEMO, tabele, wykresy, kostki, itp. Zaznacz takim polom opcję "Zajmij całą wolną przestrzeń okna". Zaznacz tą opcję także wszystkim kontenerom, w którym to pole się znajduje. Spowoduje to, że pola, które w definicji formy są wcześniej od takiego pola, będą przyklejone do lewej lub górnej części kontenera, a pola, które w definicji formy są później, będą przyklejone do prawej lub dolnej części kontenera. Zaznaczone pole rozprzestrzeni się w centralnej części na całą dostępną przestrzeń. Jeśli zmienisz rozmiar uruchomionej formy za pomocą myszy, to pola zajmujące całą wolną przestrzeń będą zmieniały swój rozmiar wraz ze zmianą rozmiaru formy. Pozostałe pola będą przyklejone do jej brzegów.

Jeśli w danym kontenerze kilku polom nadasz znacznik "Zajmij całą wolną przestrzeń okna", wtedy wszystkie te pola będą się skalować proporcjonalnie, a między nimi pojawią się splittery. Dzięki temu na uruchomionej formie za pomocą myszy będzie można sterować tym, która część formy, ile zajmie miejsca.

Zwykle zaznacz ten znacznik polom MEMO, tabelom, wykresom, kostkom, itp. W przypadku samych kontenerów jest to rzecz indywidualna dla każdego kontenera. Jeśli kontener zawiera kilka małych pól, lepiej aby sam nie zajmował całego wolnego miejsca. Jeśli kontener zawiera pole, które zajmuje całą wolną przestrzeń, lepiej aby sam też zajmował całą wolną przestrzeń, gdyż w przeciwnym razie w ogóle nie zobaczysz rezultatów zaznaczenia tego znacznika na polu wewnątrz kontenera.

Uruchomienie formy

Po zdefiniowaniu obiektu lub formy należy zapisać obiekt, a jeśli zdefiniowano lub zmodyfikowano jakąś metodę to należy skompilować na nowo bibliotekę dll dla projektu poprzez uruchomienie funkcji Skompiluj projekt z poziomu okna struktury projektu. Potrzeba zapisania obiektu jest awizowana ikoną wykrzyknika przy obiekcie, a potrzeba kompilacji projektu - ikoną wykrzyknika przy projekcie. Formę można uruchomić funkcją Uruchom okno. W ten sposób uruchamia się formę w celach testowych. W aplikacji formę uruchamia się za pomocą jednej z następujących funkcji:

  • ShowForm - podstawowa funkcja pokazująca formę na ekranie
  • ShowRecord - funkcja służy do pokazania okna edycji pojedynczego rekordu. Jeśli wywołamy formę tego samego obiektu z poziomu którego wywołujemy metodę, to przekazywany jej jest kontekst bieżącego rekordu. W przeciwnym razie kontekst należy przekazać jawnie w parametrze funkcji. Na dole formy automatycznie doklejane są przyciski Zapisz i Anuluj.
  • ShowNewRecord - funkcja służy do pokazania okna edycji w kontekście tworzenia nowego rekordu. Na dole formy automatycznie doklejane są przyciski Zapisz i Anuluj.
  • ShowDict - funkcja służy do pokazania okna słownika. Na dole formy automatycznie doklejane są przyciski Wybierz i Anuluj.

Budowanie formy z danymi

Jeśli chcesz pokazać prostą formę z tabelą z danymi z BD, zrób to w następujący sposób:

  1. Zdefiniuj nowy obiekt, nadaj mu nazwę i przypisz mu tabelę albo zapytanie SQL.
  2. Zdefiniuj w obiekcie listę pól modelu danych i koniecznie wskaż pola klucza głównego.
  3. Jeśli definiujesz zapytanie SQL to koniecznie zdefiniuj wyrażenie na FROM nawet wtedy, kiedy definiujesz metodę generującą to wyrażenie. Pierwsze budowanie zapytania może nie uruchomić metody tylko pobrać wartość stałą.
  4. Jeśli w zapytaniu SQL nie określisz metody na ORDER BY to automatycznie zostanie wygenerowane sortowanie wg pól klucza głównego.
  5. Zdefiniuj nową formę BROWSE i umieść w niej tabelę, a w tabeli umieść wybrane pola jako kolumny.
  6. Jeśli chcesz filtrować dane w sposób dynamiczny (w zależności od wartości jakiegoś parametru tego obiektu) to przypisz metodę na filtrację danych do obiektu albo do formy. Do obiektu przypisujesz metodę na filtrację, która ma działać zawsze we wszystkich formach tego obiektu. Do formy przypisujesz metodę na filtrację specyficzną dla tej konkretnej formy.

Pokaż formę na ekranie. Jeśli zobaczysz pustą tabelę mimo iż spodziewasz się jakichś danych, zwróć uwagę czy po stronie klienta w pliku dbi nie zdefiniowano filtrów EmptyFilter lub ConstFilter. Działają one także w oknach Neosa.

Zwykle nad tabelą zobaczysz przyciski + i - umożliwiające dodawanie i usuwanie rekordów. Operacje te działają w ramach bieżącej formy i mają sens albo wtedy, gdy włączysz redagowanie danych bezpośrednio w tabeli (F4) albo gdy obok tabeli umieścisz pola edycyjne. Definiując formę możesz zaznaczyć, aby forma automatycznie przechodziła w tryb edycji danych w tabeli.

Jeśli chcesz redagować dane w osobnym oknie edycyjnym zdefiniuj okno o symbolu EDIT. Okno o tym symbolu jest domyślnym oknem edycji danych i jest pokazywane po naciśnięciu przycisku + oraz dwukliku na rekordzie.

Dodając nowy rekord z poziomu własnej akcji, pamiętaj że:

  • NewRecord() - przechodzi w tryb dodawania rekordu w ramach tego samego okna z którego wywołano tą funkcję.
  • ShowNewRecord(...) - przechodzi w tryb dodawania rekordu poprzez pokazanie osobnego okna do edycji danych.

Formy niezależne od danych

Jeśli chcesz zdefiniować formę, w której można coś wyświetlić lub coś wpisać albo wybrać, ale nie a ma to nic wspólnego z żadną tabelą w BD, zrób to w następujący sposób:

  1. Zdefiniuj nowy obiekt, nadaj mu nazwę, ale nie przypisuj żadnej tabeli ani zapytania SQL.
  2. Zdefiniuj w obiekcie parametry.
  3. Zdefiniuj nową formę i umieść w niej interesujące Cię parametry.

Przykładem takiego obiektu jest SELECTPERIOD z projektu SENTE. Można przekazywać takiej formie parametry i odbierać z niej wyniki poprzez zapis i odczyt wartości parametrów obiektu.

Zagnieżdżanie form

W technologii NEOS każda forma należy do jakiegoś obiektu i może wyświetlać jedynie elementy należące do tego obiektu. W wyniku tego w naturalny sposób buduje się formy tzw. jednogridowe - to znaczy takie, które zawierają tylko jedną tabelę z danymi, wykres, kostkę i ew. pola edycyjne, itp. Nie da się na jednej formie umieścić kilku tabel w taki sposób, aby każda pochodziła z innego obiektu. Nie da się także obok siebie na jednej formie umieścić kontrolek edycyjnych ani akcji zdefiniowanych w różnych obiektach.

Nie jest to jednak ograniczeniem, gdyż technologia NEOS umożliwia zagnieżdżanie form - czyli osadzanie form jednych obiektów (podrzędnych) wewnątrz form innych obiektów (nadrzędnych). Forma podrzędna jest traktowana tak, jak każdy inny element formy nadrzędnej, a więc może być osadzona w dowolnym panelu, na dowolnej zakładce, itp. Zatem jeśli chcemy stworzyć formę dwugridową w której dane pochodzą z dwóch różnych obiektów, należy najpierw stworzyć formę obiektu podrzędnego i umieścić w niej tabelę (grida) a następnie formę obiektu nadrzędnego, także umieścić w niej tabelę (grida) a obok niego (pod nim) umieścić wskazanie do uprzednio zdefiniowanej formy podrzędnej. Aby umieścić na formie nadrzędnej wskazanie do formy podrzędnej, możemy formę podrzędną przeciągnąć (drag&drop) z listy dostępnych form. W drzewie definicji struktury formy, wskazanie do formy podrzędnej jest widoczne jako jeden z elementów formy. Nie widać w nim pełnej struktury formy podrzędnej. Operacja zagnieżdżania form działa bowiem dynamicznie - struktura formy podrzędnej nie jest kopiowana do formy nadrzędnej, ale uwzględniana dopiero w momencie tworzenia instancji formy na ekranie użytkownika. Dzięki temu każda zmiana w strukturze formy podrzędnej zostanie automatycznie odzwierciedlona we wszystkich formach, w których ta forma została zagnieżdżona.

Forma nadrzędna i forma podrzędna, mimo że na ekranie zostaną zaprezentowane jako jedna całość we wspólnym oknie, to logicznie stanowią dwa zupełnie odrębne światy. Każda z nich wyświetla własne dane własnego obiektu, odwołuje się do metod zdefiniowanych w ramach własnych obiektów, które w ogóle nie mają ze sobą powiązania. Metody jednego obiektu nie mogą przecież odwoływać się do pól innych obiektów. Nie można np w formie podrzędnej umieścić checkboxa, który będzie wpływał na filtrację danych w formie nadrzędnej - zresztą jest to mało logiczne i intuicyjne. Ale z drugiej strony, czasami chcemy na formie nadrzędnej umieścić checkboxa, który wpłynie na filtrację danych w formie podrzędnej. Jest to bardziej intuicyjne i możliwe do realizacji, chociaż zaleca się, aby każda forma działała jak najbardziej niezależnie, czyli posiadała w swojej strukturze wszystkie elementy, które rządzą jej zachowaniem.

Powiązanie między formą nadrzędną i podrzędną można zrealizować następującymi sposobami:

  • Osadzamy inną formę tego samego obiektu bez wskazywania żadnego powiązania. W efekcie obie formy działają na tym samym obiekcie, a więc dokładnie na tych samych danych. W taki sposób możemy np. osadzić okno edycyjne wewnątrz okna przeglądania i chodząc po tabeli widzimy w polach okna edycyjnego bieżący rekord, który możemy edytować.
  • Osadzamy formę innego obiektu bez wskazywania żadnego powiązania. W efekcie dane w obu częściach formy działają całkowicie niezależnie i forma zaczyna przypominać dashboard, gromadzący różne, niezależne od siebie dane.
  • Osadzamy okno podrzędne poprzez wskazanie pola relacji - czyli pola w obiekcie podrzędnym, które posiada zdefiniowaną relację na obiekt nadrzędny. W efekcie tworzy się powiązanie typu master-detail (np. nagłówki i pozycje dokumentów). Zanim takie powiązanie form będzie można zrealizować, obiekt podrzędny musi posiadać pole, które ma zdefiniowaną relację do obiektu nadrzędnego. Relacja ta stanowi bowiem klucz złączenia obu obiektów. Jeśli relacja posiada wiązania dodatkowe poprzez inne pola, to wszystkie te wiązania są używane do połączenia obiektów master i detail.

Przykład

Mamy obiekty NAGZAM i POZZAM. Jeśli pole POZZAM.ZAMOWIENIE w modelu danych ma zdefiniowaną relację na pole NAGZAM.REF to osadzając okno POZZAM.BROWSE jako detail okna NAGZAM.BROWSE wystarczy wskazać pole ZAMOWIENIE jako pole złączeniowe. Nie trzeba podawać obiektu złączenia, gdyż sama relacja mówi wszystko o złączeniu.

  • Osadzamy okno dowolnego obiektu w oknie innego obiektu poprzez wskazanie pola osadzanego obiektu oraz wskazanie innego obiektu wraz z polem złączenia, do którego chcemy się podłączyć. Jest to jedyny sposób na złączenie osadzanego obiektu z obiektem innym niż bieżący. Przydaje się jeśli budujemy okno z wielu obiektów i wielu zagnieżdżeń. Jest to jednak sposób ryzykowny, gdyż aby złączenie działało wskazany obiekt musi istnieć na zbudowanej formie i występować dokładnie jeden raz. W przeciwnym razie takie złączenie nie zadziała.

Przykład

Obiekty NAGZAM i POZZAM nie są złączone żadną relacją. Chcemy jednak powiązać zagnieżdżoną formę POZZAM.BROWSE z formą NAGZAM.BROWSE poprzez pola NAGZAM.REF i POZZAM.ZAMOWIENIE. W tym celu wskazujemy ZAMOWIENIE jako pole złączeniowe oraz wskazujemy, że obiektem relacji jest NAGZAM i pole REF.

  • Osadzamy okno dowolnego obiektu w oknie innego obiektu ze wskazaniem metody na złączenie. Jest to najbardziej elastyczna metoda, gdyż nie wymaga definiowania uprzednio żadnej relacji między polami obu obiektów i umożliwia wiązanie wg dowolnych pól w dowolny sposób. Metoda na złączenie działa w kontekście obiektu nadrzędnego, t.j. może, a nawet powinna odwoływać się do pól tego obiektu. W parametrze otrzymuje obiekt podrzędny, którym powinna sterować. Można w niej ustawić kontekst dla obiektu podrzędnego (funkcja SetContext) lub też przypisać wartości jego do parametrów, które sterują jego dziedziną.

Przykład

Robimy powyższe, ale z wykorzystaniem metody na złączenie. Metoda powinna mieć następującą treść:

public void ConnectWithPOZZAM(CustomObject detailobject)
{
  detailobject.SetContext(ZAMOWIENIE,this.REF);
}

UWAGA

Należy pamiętać o obsłużeniu przypadku gdy mamy pustą dziedzinę i odpowiednio go obsłużyć. W tym przykładzie forma podrzędna wyświetla wszystkie rekordy, które mają odpowiedni REF natomiast gdy w formie nadrzędnej REF jest pusty wyświetla wszystkie, aby temu zapobiec można użyć nieistniejącego REF, aby forma nie wyświetliła rekordów.

public void ConnectWithMWSACTS(MWSACTS detailobject)
{ 
  Contexts c = new Contexts();
  c.Add("MWSORDS", this.REF.Empty ? 0 : this.REF);
  detailobject.SetContext(c);
}

Komunikacja w oknach złożonych

W oknach złożonych z kilku obiektów biznesowych (w wyniku zagnieżdżania form typu master-detail) istnieją znaczne restrykcje w zakresie wzajemnego odwoływania się do danych oraz wywoływania metod pomiędzy różnymi obiektami leżącymi na tej samej formie. Każdy obiekt biznesowy posiada własne metody, które mogą odwoływać się jedynie do danych bieżącego rekordu tego obiektu oraz innych metod własnego obiektu. Często to nie wystarcza.

Przykład

Przyjmijmy, że na formie mamy dane jednego rekordu obiektu NAGŁÓWEK oraz tabelę z danymi obiektu POZYCJE. Jeśli w jakiejś metodzie obiektu POZYCJE chcemy odwołać się do metod lub parametrów NAGŁÓWKA, to piszemy następujący kod:

NAGLOWEK naglowek = (NAGLOWEK)GetObject("NAGLOWEK");
if(naglowek!=null)
{
  naglowek._parametr = X;
  naglowek.MetodaNaglowka();
}

Funkcja GetObject zwraca uchwyt do obiektu o zadanej nazwie w ramach tej samej formy. Jeśli obiekt danej klasy nie istnieje funkcja zwróci null. Jeśli obiekt istnieje, funkcja zwraca uchwyt do niego, a więc można operować na jego danych , parametrach oraz wywoływać jego metody.

Przykład

Jeśli pole danych RODZAJ obiektu NAGŁÓWEK ma wpływać na zachowanie obiektu POZYCJE (np na filtrację albo prezentację danych) oznacza to, że obiekt POZYCJE powinien mieć zdefiniowany parametr o nazwie _rodzaj, aby ten parametr sterował zachowaniem obiektu POZYCJE i wyglądem jego okien. Zagnieżdżając okno obiektu POZYCJE w oknie obiektu NAGŁÓWEK należy zdefiniować metodę na złączenie o takiej treści:

public void ConnectWithPOZYCJE(POZYCJE detailobject) 
{
  detailobject._rodzaj = this.RODZAJ;
}

W takim podejściu przekazywanie danych jest dwuetapowe. Najpierw w netodzie na złączenie jakaś dana z obiektu NAGŁÓWEK jest przekazywana jako parametr _rodzaj do obiektu POZYCJE, a następnie dzięki wewnętrznym metodom obiektu POZYCJE (np. metodzie na filtrację danych, metodzie na widoczność jakiejś kolumny w tabeli) ten parametr wpłynie na różne aspekty zachowania tego obiektu. Dzięki takiemu podejściu okno obiektu POZYCJE będzie działało poprawnie także wtedy, gdy nie będzie zagnieżdżone w żadnym innym oknie. Wtedy jednak wartość parametru należy ustawić z zewnątrz przez przekazanie kontekstu.

Odwołanie do ParentObject

Jeśli niniejsze okno nie jest zagnieżdżone, to ParentObject zwraca referencję do obiektu biznesowego który wywołał niniejsze okno, czyli obiektu, który wywołał to okno, np poprzez this.ShowForm(...) lub this.ShowRecord(...). Należy zwrócić uwagę, że jeśli kod wywołuje okno przez statyczne wywołanie, czyli GUI.ShowForm(...) lub GUI.ShowRecord(...), to okno wywołujące nie jest przekazywane do ParentObject nowo otwieranego okna.

Jeśli niniejsze okno jest zagnieżdżone w innym oknie, to ParentObject wskazuje zawsze obiekt najbardziej zewnętrznego okna w hierarchii zagnieżdżenia.

W komunikacji między oknami zagnieżdżonymi na jedynym oknie raczej nie powinniśmy korzystać z właściwości ParentObject.

Jak przyspieszyć otwieranie form?

Jeśli forma posiada wiele form zagnieżdżonych, to może się otwierać długo. Aby to przyspieszyć wprowadzono mechanizm buforowania otwartych form. Mechanizm polega na tym, że raz otwarta forma, podczas jej zamykania nie jest niszczona, a jedynie ukrywana. Zarówno klient jak i serwer w dalszym ciągu pamiętają jej strukturę. Ponowne otwarcie tej formy powoduje jej przywrócenie na ekranie, co trwa znacznie krócej, niż zbudowanie formy od nowa.

Aby włączyć mechanizm buforowania we właściwościach wybranej formy zaznacz checkboxa "Okno buforowane". Znacznik ten działa jedynie dla okien MDI oraz okien wolnych. Niestety nie działa on dla okien modalnych, gdyż okna modalne zawsze muszą zostać usunięte po zamknięciu.

Mechanizm działa od wersji 4.0.7 serwera Neos i wersji 4.0.6 klienta VCL. Jeśli mamy nowy serwer neosa, ale starszego klienta, mechanizm buforowania jest wyłączony.