Relacje między TForm tzw. kontekst

0

Witam,
zastanawiam się na eleganckim rozwiązaniem dla takiego pomysłu tzn.

  1. mamy formę z listą firm (data set i grid)
  2. zaznaczamy 3 firmy
  3. klikamy przycisk "wyświetl pracowników"
  4. wyświetlają się w nowym oknie (formie) pracownicy tych 3 firm

najprościej będzie dać dwa parametry do funkcji otwierającej okno z pracownikami tj. pole ID i listę wartości ID w postaci tekstu, wartości oddzielone przecinkami (wtedy sklejamy sobie SQLa np. select * from tabela where ID in (x1,x3,x5) i otwieramy dataset)

to jest najprostszy przykład, ale zastanawiam się czy nie warto tworzyć jakiegoś obiektu np. TParentFormContext, który byłby jednym parametrem funkcji otwierającej formę i przekazywał by nie tylko te dwie zmienne, ale np. całą listę zaznaczonych rekordów i inne przydatne informacje

Jakieś inne propozycje?
btw. jak ładnie skopiować taką listę zaznaczonych rekordów? VarArrayCreate?

0

jeśli nowsze delphi to np. procedure PokazPracownikow(Pracownicy: TList<Integer>) a jeśli starsze to np. napisać własną listę intów bazując na TList

0

za mało : ) jeszcze inny przypadek:
wybieram jeden rekord z firmą, przechodzę do jej pracowników i dodaje nowego prac
chciałbym np. móc podstawić część danych do formatki dodawania pracownika z danych firmy (np. adres korespondencyjny)
analogicznie np. po wejściu w edycję firmy i przejściu do prac chciałbym móc wybrać np. główna osobę do kontaktu

można by przekazywać w tej funkcji procedury, ale co z tą listą rekordów?

0
jastu napisał(a):

za mało : )

Ale co jest za mało i dlaczego?

jeszcze inny przypadek:
wybieram jeden rekord z firmą, przechodzę do jej pracowników i dodaje nowego prac
chciałbym np. móc podstawić część danych do formatki dodawania pracownika z danych firmy (np. adres korespondencyjny)

Lookup zrealizowany tak czy inaczej.
Zadajesz za dużo ogólnych pytań, na które nie ma jednej i prostej odpowiedzi.

analogicznie np. po wejściu w edycję firmy i przejściu do prac chciałbym móc wybrać np. główna osobę do kontaktu

No tu już standardowy lookup na pewno da radę.

można by przekazywać w tej funkcji procedury, ale co z tą listą rekordów?

A co ma być? przekaż sobie listę jako Variant, który będzie zawierał jeden lub więcej wartości PK (Primary Key).

0

np. customowa onShow,onClose przekazywana jako parametr (np. do walidacji wybranego rekordu przed zamknięciem formy-dziecka)
żeby lookup'nąć coś, muszę z formy sięgać do innego niż głównego dla formy ds - a nie chcę. Jak wchodzę na formę z pewnym stanem (rekordy jako parametry, nie tylko ich PK), to z takim stanem chcę na nich działać

0
jastu napisał(a):

np. customowa onShow,onClose przekazywana jako parametr (np. do walidacji wybranego rekordu przed zamknięciem formy-dziecka)

A pełnym zdaniem możesz odpowiedzieć? Bo dalej niewiele z tego rozumiem, a nie chcę mi się zgadywać.
Natomiast samą walidację, nie robiłbym w przyspawaniu do formy, tylko do konkretnego zestawu danych, który opisuje jakiś tam obiekt biznesowy.
A że akurat, dany zestaw jest dostępny dla konkretnej formy, bo ona zapewnia edycję/wyświetlanie tych danych -to inna bajka.
gdybyśmy nie pisali w Delphi, to powiedziałbym Ci MVVM, a tak muszę się produkować co to jest, jak to zrobić i dlaczego tak a nie inaczej. Szkoda gadać ;-)

żeby lookup'nąć coś, muszę z formy sięgać do innego niż głównego dla formy ds - a nie chcę.

Nie chcesz sięgać do innych DSów poza tymi, które są na formie? No to... nie mam pojęcia jak Ty chcesz to zrobić.
Natomiast same lookupy u mnie są to wysoce abstrakcyjne i specjalizowane obiekty, za którymi stoją oczywiście DSy.
Ale w pełni są zarządzane przez odpowiednią fabrykę/usługę, która dba o ich utworzenie i zarządzanie cyklem życia.

Jak wchodzę na formę z pewnym stanem (rekordy jako parametry, nie tylko ich PK), to z takim stanem chcę na nich działać

Żeby odczytać coś z bazy danych, potrzebne Ci PK - nie cały rekord.
Po co Ci cały rekord?

0

parent dla jakiejś formy może być różny, więc procedura sprawdzenia wybranego w child'zdie rekordu też może być inna tak samo jak inne możne być podpowiadanie
bardziej obrazkowy opis:
mamy formę bazową "podłoga" i dzieci "płytki" & "panele"
jeśli zaznaczę jakiś rekord na formie "podłoga", przejdę do "płytki", to zobaczę powiązane z tą podłogą płytki - przekaże rekord z min. customową procedurą onshow, która np. ukryje mi pola widoczne gry parentem jest "ściana", a pokaże te dla "podłoga"
jak będę chciał dodać nową "płytkę" lub "panel", to z przekazanego rekordu "podłoga" mogę podpowiedzieć jakieś dane przekazując customową "onNewRecord"

co do lookupów - owszem, zostaną, ale dzięki rozbudowanemu kontekstowi nie muszę z jednej formy sięgać do innej formy

0
jastu napisał(a):

parent dla jakiejś formy może być różny, więc procedura sprawdzenia wybranego w child'zdie rekordu też może być inna tak samo jak inne możne być podpowiadanie
bardziej obrazkowy opis:
mamy formę bazową "podłoga" i dzieci "płytki" & "panele"
jeśli zaznaczę jakiś rekord na formie "podłoga", przejdę do "płytki", to zobaczę powiązane z tą podłogą płytki - przekaże rekord z min. customową procedurą onshow, która np. ukryje mi pola widoczne gry parentem jest "ściana", a pokaże te dla "podłoga"

OK, ale dalej nie rozumiem po co chcesz przekazywać rekord z formy podłoga do formy płytki?
Dlaczego nie miałbyś przekazać PK z podłogi do płytek, a sama "forma" płytki nie pobierze sobie odpowiednich danych na podstawie przekazanego PK?

jak będę chciał dodać nową "płytkę" lub "panel", to z przekazanego rekordu "podłoga" mogę podpowiedzieć jakieś dane przekazując customową "onNewRecord"

Jak wyżej - po co rekord?

co do lookupów - owszem, zostaną, ale dzięki rozbudowanemu kontekstowi nie muszę z jednej formy sięgać do innej formy

Nigdy nie powinieneś sięgać z jednej formy do innej formy wprost, czyli odwołując się np. tak:

 jakasDana := formaParent.DS1.FieldByName('Pole1').Value;

Jeśli cokolwiek zmienisz na jakiejkolwiek formie, to ten mechanizm może przestać działać poprawnie.

Ale gdybyś zrobił to np. tak, czyli za pomocą dedykowanego interfejsu przekazującego dane pomiędzy formami:

type
  IDSCRecord = interface
  ['{ABD0C10D-459E-414F-8DA9-ADF1ECEFDAD9}']
    /// <summary>Tablica zawierająca wartości PK, którymi to wartościami należy odświeżyć RootDS DSControllera</summary>
    property KeyValues: Variant read GetKeyValues write SetKeyValues;
    property LookupName: string read GetLookupName write SetLookupName;
    /// <summary>Typ akcji, po której ma zostać wykonane odświeżenie rekordu</summary>
    property NotifyDBType: TDSCAction read GetNotifyDBType write SetNotifyDBType;
    /// <summary>Referencja na dataset z które pochodzi rekord</summary>
    property ReferenceDS: TADAdaptedDataSet read GetReferenceDS write SetReferenceDS;
    property ReferenceField: TField read GetReferenceField write SetReferenceField;
  end;

A sama implementacja może wyglądać np. tak:

type
  TFormBase = class(TForm)
  protected
    procedure ConsumeInitData(AInitData : IInterface); virtual; abstract;
  public
    constructor Create(AOwner : TComponent; AInitData : IInterface = nil);
  end;
implementation

TFormBase.Create(AOwner : TComponent; AInitData : IInterface = nil);
begin
   inherited Create(AOwner);
   ConsumeInitData(AInitData);
end;

Potem dla każdej formy, która dziedziczy z TFormBase musisz dostarczyć implementację metody abstrakcyjnej ConsumeInitData, czyli np. tak:

type
  TFormBase = class(TForm)
  protected
    procedure ConsumeInitData(AInitData : IInterface); override;
  end;
implementation

TFormBase.ConsumeInitData(AInitData : IInterface);
var 
  lDSCRecord : IDSCRecord;
begin
   if Supports(AInitData, IDSCRecord, lDSCRecord) then
   begin
     // openData to jakaś metoda, która otwiera źródła danych na podstawie PK.
     OpenData(lDSCRecord.KeyValues);
   end;
end;

W ten sposób masz to wszystko rozdzielone, możesz to zrobić inaczej - czyli całkowicie odseparować zarządzanie danymi od formatek (i ja mam to tak zrobione), możesz delegować odpowiednie procesy (np. otwieranie danych, sprawdzanie danych, modyfikację kontrolek, itd.) do specjalizowanego kodu.
Zgoda - jest z tym więcej zachodu na początku, ale im dalej w las z rozwojem programu, tym łatwiej się tym zarządza, rozwija i panuje.
Nie mieszaj wszystkiego ze wszystkim - czyli kodu odpowiedzialnego za otwierani danych w metodzie OnShow. W ten sposób silnie wiążesz jakąś funkcjonalność z konkretną instancją klasy - a to jest mocno niefajne.

Ja używam tego samego interfejsu (IDSCRecord) zarówno do przekazywania danych startowych jak i do zapewnienia notyfikacji dla obiektów powiązanych (np. forma lista i szczegóły; jeśli coś zmienię na formie szczegóły to lista musi się odświeżyć, a dzięki powiązaniu wie dokładnie co ma odświeżyć i kiedy).

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