Zdarzenie OnChange naszego obiektu/klasy

0

Witam,
mam pytanie czy da sie zaimplementowac klase ktora bedzie posiadala zdarzenie onchange na sobie?

Przykladowo klasa

TRzecz = class
   Nazwa: String;
   Edytowano: Boolean;
end;

chodzi o to ze jak naszemu obiektowi przypiszemy jakas wartosc NAZWY, to obiekt zaaktualizuje sie sam - ustawi Edytowano na True;

Pytanie czy sie da i jak to zrobic ewentuaalnie?

4
TRzecz = class
   private
   FNazwa:String;
   FEdytowano:Boolean;
   FOnChange:TNotifyEvent;
   procedure SetNazwa(Value:String);
   public
   property OnChange:TNotifyEvent read FOnChange write FOnChange;
   property Nazwa:String read FNazwa write SetNazwa;
   property Edytowano:Boolean read FEdytowano;
end;

procedure TRzecz.SetNazwa(Value:String);
begin
  if FNazwa<>Value then
  begin
    FNazwa:=Value;
    FEdytowano:=true;
    if Assigned(FOnChange) then FOnChange(Self);
  end;
end;
0

okej, dzieki, widze ze wystarczy uzyc setterow,

chyba sie pospieszylem z tym onchange, bo w kodzie nie rozumiem co on ma robic.. lol :)

3

Brakuje metody SetEdytowano (ech to połączenie PL i EN), która by odpalała zdarzenie zamiast robić to za każdym razem

0

Temat tworzenia własnych zdarzeń jest ściśle związany z tematyką tworzenia własnych komponentów, więc poczytaj sobie coś o tym;

Każde zdarzenie musi może być typu TNotifyEvent (nazwa typu nawet to sugeruje); Zdarzenie to nic innego, jak prywatne pole klasy, którego modyfikację umożliwią specjalne właściwości, wykorzystujace akcesory i mutatory; Jeśli zdarzenia mają być widoczne w Inspektorze Obiektów - właściwości zdarzeń muszą być zadeklarowane w sekcji Published; @_13th_Dragon pokazał przykład stworzenia zdarzeń, których właściwości zawarte są w sekcji Public, czyli nie będą widoczne w IO; Twoja klasa nie jest komponentem, więc powinieneś zadeklarować właściwości zdarzeń właśnie w tej sekcji;

Definicję zdarzenia OnChange musisz sobie sam zaimplementować; Możesz też jej w ogóle nie implementować, jeśli Twoja klasa jest klasą bazową lub abstrakcyjną - w ten sposób każda klasa dziedzicząca może posiadać własną wersję zdarzenia OnChange, robiącą różne rzeczy;

Wykorzystanie tak przygotowanego zdarzenia jest bardzo proste - sprawdzić musisz, czy prywatne pole klasy (które zawiera wskazanie na konkretną metodę) jest zainicjowane; Przyjęło się, że do tego celu używa się funkcji Assigned - przykład takiego wywołania pokazał Ci poprzednik; Na koniec, jeżeli zdarzenie zostało wcześniej zainicjowane - zostaje ono wywołane z określonymi parametrami (które mogą być różne, zależnie od typu zdarzenia); Jeżeli zdarzenie nie zostało wcześniej przypisane - nic nie wywołujesz;

To tyle - tematyka prosta, jednak poczytać trochę trzeba.

0

DObra, dzieki wszystkim za pomoc, mozna odfajkowac 2 post z kodem :) jako solucje.

2
furious programming napisał(a):

Temat tworzenia własnych zdarzeń jest ściśle związany z tematyką tworzenia własnych komponentów, więc poczytaj sobie coś o tym;
Każde zdarzenie musi być typu TNotifyEvent (nazwa typu nawet to sugeruje);

To oczywista nieprawda, podstawowym typem zdarzenia jest TNotifyEvent, ale może to być dowolny inny typ - również własny. W samym VCL/RTL są ich dziesiątki różnych typów.

Zdarzenie to nic innego, jak prywatne pole klasy, którego modyfikację umożliwią specjalne właściwości, wykorzystujace akcesory i mutatory;

No popatrz, a ja zawsze myślałem że zdarzenie to po prostu wskaźnik na procedure of object...

Jeśli zdarzenia mają być widoczne w Inspektorze Obiektów - właściwości zdarzeń muszą być zadeklarowane w sekcji Published; @_13th_Dragon pokazał przykład stworzenia zdarzeń, których właściwości zawarte są w sekcji Public, czyli nie będą widoczne w IO; Twoja klasa nie jest komponentem, więc powinieneś zadeklarować właściwości zdarzeń właśnie w tej sekcji;

A dlaczego nie published? Co za różnica dla takiej klasy, która nie dziedziczy z TComponent?

Definicję zdarzenia OnChange musisz sobie sam zaimplementować; Możesz też jej w ogóle nie implementować, jeśli Twoja klasa jest klasą bazową lub abstrakcyjną - w ten sposób każda klasa dziedzicząca może posiadać własną wersję zdarzenia OnChange, robiącą różne rzeczy;

Moim zdaniem to błąd, a w przykładowym kodzie brakuje mi metody Do - metody wywołującej zdarzenie... chodzi o coś takiego:

  TRzecz = class
  private
    FNazwa     : String;
    FEdytowano : Boolean;
    FOnChange  : TNotifyEvent;
    procedure SetNazwa(Value : String);
  protected
    procedure DoOnChange; virtual;
  public
    property OnChange  : TNotifyEvent read FOnChange write FOnChange;
    property Nazwa     : String       read FNazwa    write SetNazwa;
    property Edytowano : Boolean      read FEdytowano;
  end;

implementation

procedure TRzecz.DoOnChange;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TRzecz.SetNazwa(Value : String);
begin
  if FNazwa <> Value then
  begin
    FNazwa     := Value;
    FEdytowano := True;
    DoOnChange;
  end;
end;

Wykorzystanie tak przygotowanego zdarzenia jest bardzo proste - sprawdzić musisz, czy prywatne pole klasy (które zawiera wskazanie na konkretną metodę) jest zainicjowane; Przyjęło się, że do tego celu używa się funkcji Assigned - przykład takiego wywołania pokazał Ci poprzednik; Na koniec, jeżeli zdarzenie zostało wcześniej zainicjowane - zostaje ono wywołane z określonymi parametrami (które mogą być różne, zależnie od typu zdarzenia); Jeżeli zdarzenie nie zostało wcześniej przypisane - nic nie wywołujesz;

I właśnie dlatego zadeklarowałem metodę DoOnChange jako wirtualną; jej bazowa implementacja po prostu sprawdza czy do zdarzenia jest przypisana metoda obsługi i jeśli tak, to je po prostu wywołuje. Ale w klasie potomnej można ją łatwo nadpisać.
To taka powszechna dobra praktyka...

To tyle - tematyka prosta, jednak poczytać trochę trzeba.

A jak już czytać, to również o obserwatorach i delegatach (zdarzeniach obsługiwanych przez delegację).

0
wloochacz napisał(a)

To oczywista nieprawda, podstawowym typem zdarzenia jest TNotifyEvent

No podstawowym typem, a skoro inne także mogą być wykorzystane, to nie widzę powodu do stwierdzenia, że to nieprawda; Nie napisałem, że nie mogą być inne; To tak jak w przypadku np. TStrings - przekazywana klasa musi być TStrings, więc automatycznie oznacza to, że także można użyć zarówno TStrings, jak i TStringList czy nawet THashedStringList; No może nie dokładnie taka sama zasada, ale w tym stylu, bo nie chodzi o dziedziczenie, a o of object; Ale nie doprecyzowałem wcześniej - moja wina; W każdym razie trudno użyć jednego typu zdarzenia dla różnych parametrów - opisałem to przecież w nawiasach:

furious programming napisał(a)

Na koniec, jeżeli zdarzenie zostało wcześniej zainicjowane - zostaje ono wywołane z określonymi parametrami (które mogą być różne, zależnie od typu zdarzenia);

wloochacz napisał(a)

No popatrz, a ja zawsze myślałem że zdarzenie to po prostu wskaźnik na procedure of object...

Nigdzie nie napisałem, że to nie jest pointer na metodę; Uściślając, to procedure (Sender: TObject) of object, jeśli miałeś na myśli podstawowy typ TNotifyEvent;

wloochacz napisał(a)

A dlaczego nie published? Co za różnica dla takiej klasy, która nie dziedziczy z TComponent?

No właśnie? jakie to ma znaczenie? Nie napisałem przecież że musi; A skoro nie musi, to niech korzysta z Public, skoro nie potrzebuje udostępniania i nie jest mu potrzebne; Słowo "powinien" nie jest przecież synonimem słowa "musi";

wloochacz napisał(a)

Moim zdaniem to błąd

Ale które to błąd? Że może sobie zaimplementować ciało zdarzenia, czy że nie musi? I jedno i drugie to prawda - może, ale nie musi - zależnie od przeznaczenia, jednak nie znam wszystkich szczegółów projektu pytacza, stąd opisałem oba przypadki;

Dobrze jest sobie to opakować w metodę, jak słusznie podałeś przykład z DoOnChange - to bardzo popularna technika i szeroko stosowana; Podałem przykład jak się ją wywołuje, a nie napisałem, że koniecznie trzeba to robić ręcznie w każdej metodzie, która musi ją wywołać; Chodziło mi o wywołanie metody na którą wskazuje FOnChange - nie wiem dlaczego nie zrozumiałeś;

wloochacz napisał(a)

I właśnie dlatego zadeklarowałem metodę DoOnChange jako wirtualną;

I bardzo dobrze zrobiłeś - będzie można ją przedefiniować, czyli rozwinąłeś moje słowa, dotyczące klasy bazowej lub abstrakcyjnej;

W sumie to napisałeś prawie dokładnie to samo co ja, tylko że nieco rozwinąłeś temat i ubrałeś swoją wypowiedź w bardziej "techniczne" słowa, a ja próbowałem wytłumaczyć sprawę jak "laikowi", co dziwnie może wyglądać w oczach osób doświadczonych; Widać nie opłaca się tego robić - chyba lepiej będzie pisać od razu właściwymi słowami.

0
furious programming napisał(a):
wloochacz napisał(a)

To oczywista nieprawda, podstawowym typem zdarzenia jest TNotifyEvent

No podstawowym typem, a skoro inne także mogą być wykorzystane, to nie widzę powodu do stwierdzenia, że to nieprawda; Nie napisałem, że nie mogą być inne;

To jak już ogarniesz czytanie własnych postów ze zrozumieniem, to możemy kontynuować...
Napisałeś jak byk, cytuję:

furious programming napisał(a)

Każde zdarzenie musi być typu TNotifyEvent (nazwa typu nawet to sugeruje);

A to, powtarzam, oczywista nieprawda

To tak jak w przypadku np. TStrings - przekazywana klasa musi być TStrings, więc automatycznie oznacza to, że także można użyć zarówno TStrings, jak i TStringList czy nawet THashedStringList; No może nie dokładnie taka sama zasada, ale w tym stylu, bo nie chodzi o dziedziczenie, a o of object;

Eeeeh... mylisz pojęcia.
Deklaracja of object nie oznacza tego co napisałeś (a napisałeś o dziedziczeniu właśnie), a oznacza tyle, że procedura obsługi zdarzenia musi być metodą. Mam nadzieję, że nie muszę tłumaczyć czym różni się metoda od procedury lub funkcji?
Poza tym (w kontekście typy parametru zdarzenia TNotifyEvent), oczywiście, że chodzi o dziedziczenie - spróbuj jako Sender podać interfejs albo rekord. Skompiluje się? Oczywiście, że się nie skompiluje, ponieważ typ parametru jasno definiuje że musi to być cokolwiek co jest TObject. A więc mogą to być Twoje listy, ale i np. TForm, TDatabase i cokolwiek innego byleby dziedziczyło z TObject. A że tak się składa, że w Delphi jakakolwiek (póki co) klasa dziedziczy z TObject to zupełnie inna sprawa.

Ale nie doprecyzowałem wcześniej - moja wina; W każdym razie trudno użyć jednego typu zdarzenia dla różnych parametrów - opisałem to przecież w nawiasach:

furious programming napisał(a)

Na koniec, jeżeli zdarzenie zostało wcześniej zainicjowane - zostaje ono wywołane z określonymi parametrami (które mogą być różne, zależnie od typu zdarzenia);

wloochacz napisał(a)

No popatrz, a ja zawsze myślałem że zdarzenie to po prostu wskaźnik na procedure of object...

Nigdzie nie napisałem, że to nie jest pointer na metodę; Uściślając, to procedure (Sender: TObject) of object, jeśli miałeś na myśli podstawowy typ TNotifyEvent;

Nie - miałem dokładnie na myśli to co napisałem, nie mieszaj mi tu już z tym TNotifyEvent.

wloochacz napisał(a)

A dlaczego nie published? Co za różnica dla takiej klasy, która nie dziedziczy z TComponent?

No właśnie? jakie to ma znaczenie? Nie napisałem przecież że musi; A skoro nie musi, to niech korzysta z Public, skoro nie potrzebuje udostępniania i nie jest mu potrzebne; Słowo "powinien" nie jest przecież synonimem słowa "musi";

Napisałem jakie to ma znaczenie - w komentarzu.

wloochacz napisał(a)

Moim zdaniem to błąd

Ale które to błąd? Że może sobie zaimplementować ciało zdarzenia, czy że nie musi? I jedno i drugie to prawda - może, ale nie musi - zależnie od przeznaczenia, jednak nie znam wszystkich szczegółów projektu pytacza, stąd opisałem oba przypadki;

Błędem, moim zdaniem, jest jawne wywoływanie zdarzenia w sposób bezpośredni, bez dedykowanej wirtualnej metody.
A gdybyś ciął cytaty mniej niechlujnie i czytał wszystko, to widziałbyś o czym mowa. Bo o banalną manipulację Cię nie podejrzewam - jeszcze ;-)

Dobrze jest sobie to opakować w metodę, jak słusznie podałeś przykład z DoOnChange - to bardzo popularna technika i szeroko stosowana; Podałem przykład jak się ją wywołuje, a nie napisałem, że koniecznie trzeba to robić ręcznie w każdej metodzie, która musi ją wywołać; Chodziło mi o wywołanie metody na którą wskazuje FOnChange - nie wiem dlaczego nie zrozumiałeś;

wloochacz napisał(a)

I właśnie dlatego zadeklarowałem metodę DoOnChange jako wirtualną;

I bardzo dobrze zrobiłeś - będzie można ją przedefiniować, czyli rozwinąłeś moje słowa, dotyczące klasy bazowej lub abstrakcyjnej;

W sumie to napisałeś prawie dokładnie to samo co ja, tylko że nieco rozwinąłeś temat i ubrałeś swoją wypowiedź w bardziej "techniczne" słowa, a ja próbowałem wytłumaczyć sprawę jak "laikowi", co dziwnie może wyglądać w oczach osób doświadczonych; Widać nie opłaca się tego robić - chyba lepiej będzie pisać od razu właściwymi słowami.

Powiem Ci co ja myślę; myślę, że wiesz iż mam rację, a teraz po prostu próbujesz odwrócić kota ogonem.
Rozumiem, że pisanie półprawd i nie do końca prawdziwych tez to ma być tłumaczenie laikowi?
OK, można i tak...

0

Deklaracja of object nie oznacza tego co napisałeś (a napisałeś o dziedziczeniu właśnie), a oznacza tyle, że procedura obsługi zdarzenia musi być metodą.

Przykład z dziedziczeniem jest nietrafny - to przyznam, bo tak jak napisałem - nie chodzi o dziedziczenie, a o podobieństwo; Typów eventów się raczej nie dziedziczy, a deklaruje osobne na kształt podstawowej; Chodzi o pointer na metodę, z dowolną ilością róznych parametrów - to wiem; Wybrałem zły przykład dotyczący zasady - nie napisałem przecież, że typy eventów się dziedziczy;

Nie - miałem dokładnie na myśli to co napisałem, nie mieszaj mi tu już z tym TNotifyEvent.

To w takim razie źle zrozumiałem to, co napisałeś;

Błędem, moim zdaniem, jest jawne wywoływanie zdarzenia w sposób bezpośredni, bez dedykowanej wirtualnej metody.
A gdybyś ciął cytaty mniej niechlujnie i czytał wszystko, to widziałbyś o czym mowa.

Przecież nie uciąłem Twojego cytatu po to, żeby zamazać większość Twoich słów; Piszesz, że to co opisałem jest błędem, a napisałem nie o opakowywaniu wywołania zdarzenia w metodę, a o możliwości implementacji zdarzenia lub nie - a to zupełnie inne rzeczy;

Powiem Ci co ja myślę; myślę, że wiesz iż mam rację, a teraz po prostu próbujesz odwrócić kota ogonem.

Wiem, że masz rację i nie mam co do tego wątpliwości; Natomiast część jest półprawdą, a część jest nieprecyzyjna, co w poprzednim poście doprecyzowałem, jednak Ty wolisz twierdzić, że odwracam kota ogonem; Jak uważasz.

0

muszę tłumaczyć czym różni się metoda od procedury lub funkcji?

to poproszę jeszcze, bo dyskusja ciekawa a ja siedze w delphi dopiero od pol roku.

0
dudi1223 napisał(a):

muszę tłumaczyć czym różni się metoda od procedury lub funkcji?

to poproszę jeszcze, bo dyskusja ciekawa a ja siedze w delphi dopiero od pol roku.

A przedtem w czym siedziałeś? W czymś co nie ma podstaw OOP? ;-)

W skrócie - metoda to dowolna procedura (lub funkcja, bo to taki pascalowy "ficzer"), która została zadeklarowana w klasie.
A więc metoda, to procedura która jest częścią klasy i jest dostępna tylko poprzez obiekt tej klasy (dla purystów - tak wiem, że są tzw. metody statyczne, które są wołane z kontekstu klasy - a nie obiektu).

Jak wiesz (a może nie wiesz) w Delphi da się napisać kod czysto proceduralnie, bez wszystkich ficzerów obiektowości.
Jeśli spotkasz takich, którzy stwierdzą "to bzdura - bo nawet kod zdarzenia OnClick jest metodą obiektu", to powiedz im, że oni nie programują obiektowo, a tylko używają obiektów.
A to zasadnicza różnica.

A teraz drugie pytanie - wiesz czym różnią się klasa od obiektu?

0

Obiekt to instancja klasy. :), podstawy jakieś mam, staram sie pisac obiektowo, ale przyznaje ze stawiam na czas wykonania problemow a nie jakosc kodu. :)

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