Blokada kliknięcia przycisku

0

Tytuł dość kontrowersyjny, ale jakoś tak wyszło.

Chodzi o taką sytuacją:

Jest pole typu Edit i jest przycisk. User wpisuje jakąś wartość w pole edit. Przy zdazeniu onEditValueChanged (czyli po zmianie wartości względem poprzedniej w polu Edit - tak na prawde po wyjściu z pola) wykonywane jest sprawdzenie - czy wartość aktualnie znajdujaca się w polu edit odpowiada dokladnie jednemu rekordowi w tabeli - jezeli tak - wykonywane są pewne operacje, które nas tutaj nie interesują.

Jeżeli wprowadzono taką wartość, która nie odpowiada wprost ani jednej pozycji lub wielu pozycjom - zdarzenie onEditValueChaged wywoluje zdarzenie onClick na Buttonie, ktore wywoluje okienko z lista wyboru - przefiltrowana względem wartości z pola Edit.

Wszystko jest fajnie, do czasu gdy próbujemy wykonac operację taką:

wprowadzam jakąś "ogólna" wartość odpowiadającą wielu rekordom i klikam przycisk. W tej sytuacji onEditValueChanged odpala raz onClick, a potem wykonuje się onClick z przysku - czyli okienko z lista wyboru pojawia się dwa razy.

Znów - gdy po wykonaniu onClick w onEditValueChanged dodam semafor (np. ExitFromEdit : Boolean = TRUE), że onClick na samym początku sprawdza czy semafor jest opuszczony: ExitFromEdit = FALSE - wtedy wykonuje operację i dodatkowo na końcu onClicka niezależnie od wyniku sprawdzenia na początku dam ExitFromEdit:=FALSE; to efekt jrst taki, że jak wprowadze wartość w Edit i wykona się onEditValueChanged (po którym jest ExitFromEdit:=TRUE) to owszem okno pojawi się raz, ale pierwsze kliknięcie na Button - powoduje brak reakcji (bo jest sprawdzenie stanu semafora i na koncu ustawienie na sztywno ExitFromEdit:=FALSE) i dopiero drugie kliknięcie pozwala wykonać operacje typowe dla onClick Buttona.

Ufff ... ja wiem, że opis mi się zagmatwał, ale może ktoś coś zrozumie z tego miszungu. Generalnie jest "prawie dobrze" - gdyby udalo sie jeszcze zlikwidować ten nieszczęsny brak reakcji po pierwszym kliknięciu.

Miał ktoś kiedyś podobną zagwozdke logiczną ?

0

Może po prostu:

if not forma.visible then forma.Show;

Bo to rozwiązanie chyba nie może zfailować?

0
toyman napisał(a)

W tej sytuacji onEditValueChanged odpala raz onClick, a potem wykonuje się onClick z przysku - czyli okienko z lista wyboru pojawia się dwa razy.

tego nie lapie...?
masz edit i przycisk. w onEditValueChanged masz button.Click;
a w onClick buttona form.show;

i forma pojawia sie dwa razy?

0

Przepraszam. Zagmatwałem. Jest tak:

Edit -> onEditValueChange

Button -> onClick

kiedy user wpisuje jakąś wartość i opuszcza pole (np. Tabulatorem) odpalane jest zdarzenie onEditValueChange. Sprawdzam wtedy czy wprowadzona wartość odpowiada jakiejś jednej konkretne wartości z konkretnej tabeli (odpalam zapytanie SQL). Jeżeli tak - w drugim polu Edit pojawia się opis do tej wprowadzonej wartości - np. po wprowadzeniu konkretnego nr PESEL - w drugim Editcie pojawia się Imię i nazwisko.

Wizualnie jest tak: | EDIT1 - wartość | Button (...) | EDIT2 - opisowy |

Teraz - jeżeli wprowadzimy część wartości i i ta wartość nie odpowiada wprost jednemu rekordowi (jest wiele odpowiedników lub brak odpowiedników) - wywoływane jest zdarzenie Button->onClick, które pokazuje okienko z listą dostępnych wartości - z tym, że lista jest już przefiltrowana już wstępnie. np. jeżeli wprowadzimy pierwsze trzy cyfry nr PESEL - pojawia się okienko z listą osób, których PESELe zaczynają się tak jak wprowadzona wartość. User wybiera interesującą wartość z listy i ta przepisuje się do Edita (potem jest jeszcze raz wykonywane sprawdzenie czy wprowadzona wartość odpowiada dokładnie jednej wartośc - wiem glupota, ale na razie nie chce rozbijać aplikacji, która robi swoją robotę)

Kłopot pojawia się w momencie, kiedy User wprowadzi część wartości (np. pierwsze trzy cyfry nr PESEL) i od razu kliknie przycisk (Button) - wtedy odpala się w pierwszej kolejności zdarzenie Edit -> onEditValueChanged, a później Button -> onClick.

teraz jeżeli utworzę sobie jakąś zmienną globalną np. ExitFromEdit i w zdarzeniu onEditValueChange po wywolaniu zdarzeni onClick ustawię ją na TRUE, a w zdarzeniu onClick sprawdzę czy ExitFromEdit = TRUE - nie wykonuję treści onClick. Jednakże na końcu - poza if-em daję ExitFromEdit:=FALSE.

Taki układ generalnie działa - ale. Jeżeli wyjdę z edita Tabem (nie klikam Buttona) - pozostaje wartość TRUE w ExitFromEdit - pochodzę trochę po aplikacji, poklikam w inne pola, powprowadzam inne dane, a następnie stwierdzam, że jednak chciałbym wywolać okienko spod Buttona -> onClick - za pierwszym kliknięciem nadal mam ExitFromEdit = TRUE. Na końcu przestawiam zawsze na FALSE więc drugie kliknięcie pozwala na wykonanie treści onClick.

No dobra to teraz bardziej konkretnie:

Zdarzenie onEditValueChanged - pesel = Edit

procedure Tfrm_Glowne.peselPropertiesEditValueChanged(Sender: TObject);
begin
    if pesel.Text<>'' then begin
        Pacjent.SQL.Clear;
        Pacjent.SQL.Add('SELECT  p.nazwisko ,p.imiona ,p.data_ur ,p.pesel ,p.plec ,p.miasto ,p.ulica ');
        Pacjent.SQL.Add('       ,p.nr_domu ,p.nr_lokalu ,p.id_pac ,p.id_dodatkowy ,p.status_nr_pesel ');
        Pacjent.SQL.Add('       ,p.uprawnienie ,p.uprawnienia_po ,p.status_potw_ub ,p.kod_kasa_ch  ');
        Pacjent.SQL.Add('       ,COALESCE(CAST(p.nr_karty_ubezp AS VARCHAR(12)),'''') || COALESCE(CAST(p.nr_dupl_karty_ub AS VARCHAR(12)), '''')   AS nr_karty ');
        Pacjent.SQL.Add('       ,CAST(p.nazwisko || '', '' || p.imiona AS VARCHAR(52)) AS pacjent ');
        Pacjent.SQL.Add('FROM pacjent p ');
        Pacjent.SQL.Add('WHERE (czy_archiwalny=0) ');
        Pacjent.SQL.Add('  AND UPPER(p.pesel)=UPPER('''+pesel.Text+''') ');
        Pacjent.Active:=TRUE;
        LogDebug(Pacjent.SQL.Text);
        if not Pacjent.Eof then begin
                pesel.Text:=Pacjent.FieldByName('pesel').AsString;
                cxTextEdit1.Text:=Pacjent.FieldByName('pacjent').AsString;
        end else begin
            JvXPButton1Click(Self);
            ReturnFromPacjent:=TRUE;
        end;
    end else begin
        ClearPacjent;
    end;
end; 

Button - onClick

procedure Tfrm_Glowne.JvXPButton1Click(Sender: TObject);
begin
    if not ReturnFromPacjent then begin
        if Tabelka=nil then
            Tabelka:=TTabelka.Create(Self);
        if Tabelka<>nil then begin
            Tabelka.Czysc;
            Tabelka.tytul:='Pacjenci';
            Tabelka.dopasuj:=TRUE;
            Tabelka.Query.SQL.Clear;
            Tabelka.Query.SQL.Add('SELECT  p.nazwisko, p.imiona, p.data_ur, p.pesel, p.plec, p.miasto, p.ulica ');
            Tabelka.Query.SQL.Add('       ,p.nr_domu, p.nr_lokalu, p.id_pac, p.id_dodatkowy ');
            Tabelka.Query.SQL.Add('       ,COALESCE(CAST(p.nr_karty_ubezp AS VARCHAR(12)),'''') || COALESCE(CAST(p.nr_dupl_karty_ub AS VARCHAR(12)), '''')   AS nr_karty ');
            Tabelka.Query.SQL.Add('FROM pacjent p ');
            Tabelka.Query.SQL.Add('WHERE (czy_archiwalny=0) ');
            if pesel.Text<>'' then begin
                Tabelka.Query.SQL.Add('  AND ( ');
                Tabelka.Query.SQL.Add('          (p.nazwisko || p.imiona LIKE '''+pesel.Text+'%'') ');
                Tabelka.Query.SQL.Add('       OR (p.pesel LIKE '''+pesel.Text+'%'') ');
                Tabelka.Query.SQL.Add('      ) ');
            end;
            Tabelka.Query.SQL.Add('ORDER BY UPPER(nazwisko), UPPER(imiona) ');
            Tabelka.Query.Active:=TRUE;
            LogDebug(Tabelka.Query.SQL.Text);
            if Tabelka.Query.Active then begin
                //--------------------------------------------
                Tabelka.IDField.Clear;
                Tabelka.IDField.Add('pesel');
                //--------------------------------------------
                Tabelka.ToggleVisible('id_pac',FALSE);
                Tabelka.ToggleVisible('id_dodatkowy',FALSE);
                //--------------------------------------------
                Tabelka.SetColumnCaption('nazwisko','Nazwisko');
                Tabelka.SetColumnCaption('imiona','Imię');
                Tabelka.SetColumnCaption('data_ur','Urodzony');
                Tabelka.SetColumnCaption('pesel','PESEL');
                Tabelka.SetColumnCaption('plec','Płeć');
                Tabelka.SetColumnCaption('miasto','Miejscowość');
                Tabelka.SetColumnCaption('ulica','Ulica');
                Tabelka.SetColumnCaption('nr_domu','Nr domu');
                Tabelka.SetColumnCaption('nr_lokalu','Nr lokalu');
                Tabelka.SetColumnCaption('id_pac','id_pac');
                Tabelka.SetColumnCaption('id_dodatkowy','id_dodatkowy');
                //--------------------------------------------
            end;
            if Tabelka.ShowModal=mrOK then begin
                if Tabelka.num_wynik>=0 then begin
                    pesel.Text:=Tabelka.wynik[0][0];
                end;
            end;
        end else begin
            Application.MessageBox('Błąd podczas wywoływania okna dialogowego','Błąd wywołania okna',MB_ICONERROR+MB_OK);
        end;
    end;
    ReturnFromPacjent:=FALSE;
end;
 
0
JvXPButton1Click(Self);

Źle, powinno być JvXPButton1.Click;.

Co do problemu to masz pare wyjść:
1.Ustawiać flagę która wywołuje okienko, a która jest sprawdzana (i wywoływane jest okienko) np. w timerze.
2.Ustawiać flagę która powstrzymuje wyskakiwanie okienka i która jest resetowana np. w timerze.

Prawie to samo, ale polecam drugie rozwiązanie :P .

0
Lulhax napisał(a)
JvXPButton1Click(Self);

Źle, powinno być JvXPButton1.Click;.

  1. Komponent którego używam TJvXPButton z biblioteki JEDI nie ma metody Click;
  2. Czym różni się symulacja naciśnięcia LPM od wywołania zdarzenia uruchamianego po kliknięciu LPM ? To znaczy pytam dlatego, że od zawsze, wszystkie programy jakie pisałem - pisałem waśnie w ten sposób - oprogramowywałem zdarzenia, a później w miarę potrzeb wywoływałem je traktując jak procedury. To co mi przedstawiłeś po pierwsze wywraca mój sposób pojmowania aplikacji Delphi, a po drugie odnosi się tylko do tych komponentów, które mają medoty symulujące uruchomienie jakiegoś zdarzenia. Pojaśnij dlaczego bładzę (bez uszczypliwości).
Lulhax napisał(a)

Co do problemu to masz pare wyjść:
1.Ustawiać flagę która wywołuje okienko, a która jest sprawdzana (i wywoływane jest okienko) np. w timerze.
2.Ustawiać flagę która powstrzymuje wyskakiwanie okienka i która jest resetowana np. w timerze.

Prawie to samo, ale polecam drugie rozwiązanie :P .

A jak zachowa się timer, kiedy aplikacja jest uruchamiana np. poprzez TeamViewera lub LogMeIna na bardzo słabym łączu - gdzie okna odświeżają się kilka-kilkanaście sekund ? Oczywiście - jest to jakieś rozwiązanie, ale jakoś mnie nie przekonuje.

0

Pojaśnij dlaczego bładzę (bez uszczypliwości).

Widać po coś jest metoda Click skoro ją zrobili. Jest to łatwiejsze do zrozumienia a może wywoływać inne procedury które coś robią i w ten sposób je obchodzisz. A jeśli takowych procedur nie ma to zapis po optymilizacji jest taki sam, ale bardziej czytelny dla programisty. Nie żebyś robił jakiś straszny błąd, po prostu tak się powinno generalnie robić. Coś jak używanie tabulatorów - nie trzeba, ale warto.

A jak zachowa się timer, kiedy aplikacja jest uruchamiana np. poprzez TeamViewera lub LogMeIna na bardzo słabym łączu - gdzie okna odświeżają się kilka-kilkanaście sekund ? Oczywiście - jest to jakieś rozwiązanie, ale jakoś mnie nie przekonuje.
Co ma timer do TeamViewera czy innego Hamachi? Kolego, może najpierw dowiedz się o czym piszesz, bo albo się mylę, albo pierniczysz głupoty.
Wyjaśniam: Timer to zdarzenie które jest wywoływane co dany okres czasu, który wykonuje pewne operacje (niekoniecznie graficzne, tutaj akurat niegraficzne). TeamViewer czy Hamachi wysyła obraz, timer jest wykonywany na lokalnej maszynie, i jeśli obraz nie ulega aktualizacji to działanie timera jest zupełnie niewidoczne dla takich programów. Tego typu programy nie kopiują bloków danych programu etc., tylko odczytują obraz i wysyłają ruchy myszki i naciśnięcia klawiatury. Więc timer nic nie zmienia.

poza tym, timery stosuje się często, i nie wiem skąd ta twoja niepewność co do nich.

0
Lulhax napisał(a)

Pojaśnij dlaczego bładzę (bez uszczypliwości).

Widać po coś jest metoda Click skoro ją zrobili. Jest to łatwiejsze do zrozumienia a może wywoływać inne procedury które coś robią i w ten sposób je obchodzisz. A jeśli takowych procedur nie ma to zapis po optymilizacji jest taki sam, ale bardziej czytelny dla programisty. Nie żebyś robił jakiś straszny błąd, po prostu tak się powinno generalnie robić. Coś jak używanie tabulatorów - nie trzeba, ale warto.

No może i po coś on jest w TButton. W TJvXPButton go nie ma. Idąc twoim tokiem myślenia i argumentacją - widać niepotrzebne.

Lulhax napisał(a)

A jak zachowa się timer, kiedy aplikacja jest uruchamiana np. poprzez TeamViewera lub LogMeIna na bardzo słabym łączu - gdzie okna odświeżają się kilka-kilkanaście sekund ? Oczywiście - jest to jakieś rozwiązanie, ale jakoś mnie nie przekonuje.
Co ma timer do TeamViewera czy innego Hamachi? Kolego, może najpierw dowiedz się o czym piszesz, bo albo się mylę, albo pierniczysz głupoty.
Wyjaśniam: Timer to zdarzenie które jest wywoływane co dany okres czasu, który wykonuje pewne operacje (niekoniecznie graficzne, tutaj akurat niegraficzne). TeamViewer czy Hamachi wysyła obraz, timer jest wykonywany na lokalnej maszynie, i jeśli obraz nie ulega aktualizacji to działanie timera jest zupełnie niewidoczne dla takich programów. Tego typu programy nie kopiują bloków danych programu etc., tylko odczytują obraz i wysyłają ruchy myszki i naciśnięcia klawiatury. Więc timer nic nie zmienia.

poza tym, timery stosuje się często, i nie wiem skąd ta twoja niepewność co do nich.

No. Akurat o zachowaniu i TeamViewera i LogMeIna (nie LogMeIn Hamachi przyjacielu, bo ten tworzy całkiem sprytne tunele VPNowe, któe nijak nie służą do przesyłania senso-stricte obrazu) mogę z tobą podyskutować. Od kilku lat zajmuję się zdalnym serwisowaniem klientów właśnie za pomocą takich narzedzi. I wyobraź sobie, że to co mnie wydawało się niemożliwe (np. kliknięcie czegokolwiek zanim odświeży się do końca okno = Update/Refresh/Repaint) poprzez te programy staje się realne. Nie raz byłem w stanie wykonać operację, która na desktopie, lokalnie była nie do odtworzenia, bo ekran nawet nie mignął - na TV i LogMeIn odświeża się (szczególnie po łączach typu Blueconnect, Play Mobile czy iPlus) tak wolno, że pojawiają sie błędy, o których ani Tobie, ani mnie się nie śniło.

EDIT: Chodziło mi o coś takiego: pojawia się okienko. Na nim przycisk który jest Enabled = TRUE. Za chwile przycisk jest Enabled = FALSE (to samo na desktopie jest niezauważalne). Na łączu typu Blueconnect czy PlayMobile czy iPlus przejście z jednego do drugiego stanu trwa tyle, że jestem w stanie kliknąć ten przycisk zanim on stanie się Enabled = FALSE. Tak samo z polami Edit czy Memo. Odpala się okno, pojawia się Edit/Memo.Readonly = FALSE, po czym przestawia się na ReadOnly = TRUE. Na desktopie jest pstryk. Tutaj jesteś w stanie wkleić tekst w to pole.

Uwierz (a na prawdę widziałem już kilka dziwnych zachowań różnych aplikacji), że mój sceptycyzm nie jest bezzasadny.

0

@toyman - duuzo piszesz i w efekcie znow sie gubie.
jesli dobrze rozumiem, to problem wyglada w skrocie tak:

wklepujesz cos to edita.
po "opuszczeniu" edita, ten wywoluje onEditValueChanged.
wewnatrz tej procedury pokazujesz okienko.
to okienko zmienia wartosc w edicie.
zmiana wartosci w edicie znow wywoluje onEditValueChanged (a nie powinno bo tego nie chcesz).

czy tak?

jesli tak to moze po prostu na czas zmieniania wartosci w edit, odlacz od niego event:

   pesel.OnEditValueChanged:=nil;
   pesel.Text:=Tabelka.wynik[0][0];
   pesel.OnEditValueChanged:=peselPropertiesEditValueChanged;

w ten sposob, zmieniajac zawartosc edita, nie zareaguje on na ta zmiane.

0

No może i po coś on jest w TButton. W TJvXPButton go nie ma. Idąc twoim tokiem myślenia i argumentacją - widać niepotrzebne.

Skoro go nie ma to trudno :P .

No. Akurat o zachowaniu i TeamViewera i LogMeIna (nie LogMeIn Hamachi przyjacielu, bo ten tworzy całkiem sprytne tunele VPNowe, któe nijak nie służą do przesyłania senso-stricte obrazu) mogę z tobą podyskutować. Od kilku lat zajmuję się zdalnym serwisowaniem klientów właśnie za pomocą takich narzedzi. I wyobraź sobie, że to co mnie wydawało się niemożliwe (np. kliknięcie czegokolwiek zanim odświeży się do końca okno = Update/Refresh/Repaint) poprzez te programy staje się realne. Nie raz byłem w stanie wykonać operację, która na desktopie, lokalnie była nie do odtworzenia, bo ekran nawet nie mignął - na TV i LogMeIn odświeża się (szczególnie po łączach typu Blueconnect, Play Mobile czy iPlus) tak wolno, że pojawiają sie błędy, o których ani Tobie, ani mnie się nie śniło. [...]

Racja, Hamachi to co innego, mój błąd.
Nadal wydaje mi się niemożliwe żeby aplikacja kombinowała z timerami, a zwłaszcza że sam używałem TeamViewera do połączeń z moimi aplikacjami które stosują timery - nic złego się nie działo. Słyszałem o tym że niektóre aplikacji symulują komponenty na formie, ale to i tak nie ingeruje w timery. Skoro 'coś' ingeruje w Twoje timery i doprowadza do ich błędów to widać to 'coś' jest złe, bo timery są stosowane praktycznie wszędzie. I Twój sceptycyzm wydaje mi się dziwny. Możesz kombinować w użycie wątków zamiast timerów, ale timery to PODSTAWA. Nie przekonasz mnie że aplikacje pokroju TeamViewer ingerują w to, gdyż to: 1. Jest bez sensu. 2.Jest bez sensu. 3.Powoduje same problemy, nic nie rozwiązuje.

Skoro nie timer, to baw się w jakiś często używany event ...

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