Kommunikation zwischen Xbp-Objekten

Auf dem Weg von Clipper, FoxPro u.ä. nach Xbase++

Moderator: Moderatoren

Antworten
heidie
Rookie
Rookie
Beiträge: 15
Registriert: Mo, 18. Jun 2007 9:45
Wohnort: Kandel

Kommunikation zwischen Xbp-Objekten

Beitrag von heidie »

Hallo zusammen,

ich lese nun schon einige Zeit im Forum so rum, aber einen Antwort auf mein Problem habe ich dabei nicht gefunden, möglicherweise auch wegen mangelnden Wissens.

Ich habe da eine CRT-Anwendung die von Clipper S87 den Weg nach Xbp in den Hybritmodus gemacht hat.

Weil der Kunde mehr Komfort wünschte, habe ich den Teil zum editieren von Langtexten neu programmiert.

Letztlich nur ein Dialog mit 2 Pushbuttons und einem MLE. Nun soll noch eine Druckfunktion hinzukommen. Dabei sehe ich dank Huberts Druckerlib kein Problem.

Zu meinem Problem: Die PB heißen "Schließen" und "Änderung verwerfen"
Der Button schließen speichert einen eventuell veränderten Text.

Der Button "Änderung verwerfen" soll eigentlich erst aktiv werden wenn im MLE eine Änderung des Textes eingetreten ist. -> kein Plan wie die kommunizieren ???
Derzeit ist der halt immer Aktiv und steuert über eine lVar das Verwerfen der Änderungen.

Nun will der Kunde den Text drucken. Der Druckprozess muss ja auch die aktuellen Änderungen mit drucken, die ja ggf noch im Puffer des MLE stehen.

Hier mal der Code, der erst ab den erzeugen des XbpDlg interessant wird. Das Ding läuft soweit stabil, aber eben nicht so, wie ich es mir vorgestellt habe.

Mir interessiert dabei was sich die Alaska Leute zu diesem Thema gedacht haben. Denn die Statusabhängikeiten der verschiedenen Objekte gibts doch zuhauf und ich scheitere immer wieder an dem gleichen Dingen. Ich kappiere dabei einfach nicht, wieso diese essentiellen Dinge so wenig beschrieben sind. Oder ich raffe das trotz lesen nicht.

Code: Alles auswählen

*** Name...........: FUNCTION Langtxt (Pflege der Produkt-Langtexte)
*** erstellt am....: 27.11.1992
*** letzte Žnderung: 28.12.1998
*** ##########################################################################
** Aufruf       = Langtxt(<expC1>,<expL>,<expC2>)
** Wirkung      = Pflege der Produkt-Langtexte
** Parameter    = <expC1> = Produktnummer zum zuordnen der Texte
**              = <expL > = kopieren des LT erlauben
**              = <expC2> = Art des Textes neu 19.07.96
** R
Ciao

Heinz-Dieter aus Kandel in der schönen Südpfalz
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

wenn du ein MLE mit datalink ausstattest und getdata() aufrufst, wird der Inhalt gespeichert.
Insoweit ist dein replace später doppelt gemoppelt - eventuell schon mit falschen Daten !
Außerdem ist es nicht sinnvoll (auch wenn Alaska das im functionscode so macht) alle Controls in der gleichen Variablen (hier oMLE) aufzubauen, da man diese sonst nur noch in der Childlist suchen muss)

Wenn es - wie du schreibst - nur ein einzelnes Fenster mit MLE ist, dass nur in diesem Datensatz (also ohne blättern) aktiv sein soll, würde ich keinen datalink setzen, sondern einfach im Create vor show oMLE:setData(cTxt) aufrufen.

Dem MLE würde ich einen oMLE:keyboard := {|x1,x2,oXbp| oPBSave:enable() } spendieren, das schaltet bei jedem Tastendruck den Pushbutton ein, etwas unschön, aber mir fällt bis jetzt nichts besseres ein und SO schnell wirst du ja auch nicht tippen ;-)

Beim oPBSave:activate() würde ich eine Methode / Funktion aufrufen die ganz normal mit replace die Daten speichert. Ich würde auch keinen CLOSE-EVENT erzeugen (wer weiß welches Fenster den abbekommt ;-) ) sondern gleich oDlg:destroy() aufrufen, nachdem alles geschlossen wurde. Ich selbst nutze übrigens fast ausschließlich CLASS Code und schreibe immer eine Methode CLOSE in der ich alles regle.
Gruß
Hubert
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16502
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Hubert,
brandelh hat geschrieben:Dem MLE würde ich einen oMLE:keyboard := {|x1,x2,oXbp| oPBSave:enable() } spendieren, das schaltet bei jedem Tastendruck den Pushbutton ein, etwas unschön, aber mir fällt bis jetzt nichts besseres ein und SO schnell wirst du ja auch nicht tippen ;-)
warum? Ist doch nicht sooo schlecht :wink:
Man könnte höchstens vorher prüfen, ob der Button nicht bereits aktiv ist:

Code: Alles auswählen

oMLE:keyboard := {|x1,x2,oXbp| iif( .not. oPBSave:IsEnabled(), oPBSave:enable(), ) }
Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Rolf Ramacher
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1930
Registriert: Do, 09. Nov 2006 10:33
Wohnort: Bergheim
Danksagung erhalten: 3 Mal
Kontaktdaten:

Beitrag von Rolf Ramacher »

Hi,

wie Hubert schon sagte, wenn datalink vorhanden ist, wird die Tastatureingabe automatisch in die Variable cText geschrieben.

Ich habe hierfür dann auch 2 Pushbutton. Bei "Abbruch" oder "Zurück"
ist nur AppEventClose - bei "Speichern" wird eine Funktion aufgerufen, die
dann cText in die richtige Variable in die Datenbank schreibt.
Gruß Rolf

Mitglied der Gruppe XUG-Cologne
www.xug-cologne.de
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16502
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Hubert,
ich habe gerade beim Duschen noch mal drüber nachgedacht - ganz so einfach können wir uns das doch nicht machen!
Der keyboard-slot ist zwar schon die richtige Stelle, aber im Moment würde der Knopf auch aktiviert werden, wenn man nur den Cursor versetzt (ein Zeichen links oder rechts oder eine Zeile runter oder hoch).
Man muss also in dem Slot eine Methode aufrufen, die den aktuellen EditBuffer() des MLEs mit dem ursprünglichen vergleicht. Und nur bei einem Unterschied den Button enablen.
Also am Besten nach dem SetData() am Anfang den EditBuffer in einer Variablen merken, damit man diese später mit dem dann aktuellen EditBuffer() abgleichen kann.

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo,
Man muss also in dem Slot eine Methode aufrufen, die den aktuellen EditBuffer() des MLEs mit dem ursprünglichen vergleicht. Und nur bei einem Unterschied den Button enablen.
Genau! Oder so:

Code: Alles auswählen

oMLE:keyBoard := { |nKeyCode,u,o| iif( o:changed, oPB:enable(), NIL ) }
Nur was ist, wenn der User mittels Mouse-Copy/Paste eine Änderung im Text vornimmt!?

Ich bin auch der Ansicht, dass es einen Change-Callback geben müsste, der ausgelöst würde, wenn in einem editierbaren Xbp (SLE/MLE...) eine Änderung (egal von wo: Mouse, Keyboard oder ...) erfolgt.
Beste Grüße,
Sören
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

ich bin mir nicht sicher ob die Tasten auf und ab (Sondertasten) noch im keyboard Block ankommen. Ich dachte alle solchen Tasten werden geschluckt. Ansonsten ist die Abfrage nach CHANGED natürlich sinnvoll, allerdings hatte ich schon damit Probleme ... was war das noch ...

Ja, wenn ich eine Vorgabe in das Feld schreibe (setdata()) ist changed auf .f. (was im Prinzip ja richtig ist), aber in vielen Fällen möchte man diese Vorgabe doch speichern. Ich meine man kann heute in diesen Fällen changed := .t. setzen, bin mir jetzt aber nicht sicher.

Eine Vergleichsvariable ist auf jeden Fall eine sichere Sache, ein changed-Callbackslot wäre natürlich ab Besten ;-)

Aber Achtung, beim Abfragen des Inhaltes nicht getdata() sondern editbuffer() nutzen, sonst wird schon automatisch gespeichert (wenn der datalink ausgefüllt ist).
Gruß
Hubert
heidie
Rookie
Rookie
Beiträge: 15
Registriert: Mo, 18. Jun 2007 9:45
Wohnort: Kandel

Beitrag von heidie »

Hallo,

danke mal für die Antworten, ich hab das nun ca. 5 mal gelesen, bin aber nicht unbedingt schlauer geworden. Macht aber im Moment nichts.

Ich gleiche die Infos aus den Antworten mit dem Handbuch ab. Programmiere was um und dann sehen wir weiter.

Im Moment sind meine Gedanken eher bei "Öffentlichen Schauen" und beim Fußball.

Melde mich wieder.

Für nachher drücke ich uns die Daumen.
Ciao

Heinz-Dieter aus Kandel in der schönen Südpfalz
heidie
Rookie
Rookie
Beiträge: 15
Registriert: Mo, 18. Jun 2007 9:45
Wohnort: Kandel

Beitrag von heidie »

Hallo,

habe nun den Code etwas umgebaut und dabei eure Vorschläge mit eingearbeitet.

@Hubert: die Objekte haben nun eigene Var-Namen. Dadurch können ja die einzelnen Objekte besser angesprochen werden.

@Sören: habe mir erlaubt deinen Codevorschlag zu verwenden. Die Nichtfreischaltung bei Einfügen aus der Zwischenablage via Maus kann dabei vernachlässigt werden.

Code: Alles auswählen

   LOCAL oDlg
   LOCAL oPB_s,oPB_v,oMLE,oPB_d
   LOCAL nEvent, mp1 := 0, mp2 := 0, oXbp := ""
   ...
   // MLE erzeugen, Position bei :create() angeben
   // und Datencodeblock mit Zugriff auf LOCAL Variable erzeugen
   oDlg         := XbpDialog():new( AppDesktop() ,, aPos,aSize )
   oDlg:title := cUeTxt
   oDlg:taskList := WahR
   oDlg:create()
   //
   oMLE := XbpMLE():new(oDlg)
   oMLE:wordWrap := .F.
   oMLE:horizScroll := .F.
   oMLE:ignoreTab := WahR
   //oMLE:dataLink := {|x| IIf( x==NIL, cText, cText := x ) }
   oMLE:keyboard := {|x1,x2,oDlg| IIF(!oPB_v:IsEnabled(), oPB_v:enable(), ) }
   oMlE:setFontCompoundName(FONT_COURIER_MEDIUM)
   oMLE:create( , , {2,60}, {662,301} )
   oMLE:setData(cText)
   //
   oPB_s := XbpPushButton():new(oDlg, ,{INT(aSize[1]-165),2},{155,26})
   oPB_s:caption  := "~Schlieáen"
   oPB_s:activate := {||PostAppEvent(xbeP_Close)}
   oPB_s:create()
   //
   oPB_d := XbpPushButton():new(oDlg, ,{INT(aSize[1]-330),2},{155,26})
   oPB_d:caption  := "D~rucken"
   oPB_d:activate := {||DrProLTxt(oMLE:getData(),{produkt->prod_1,produkt->prod_2,produkt->prod_3})}
   oPB_d:create()
   IF !EMPTY(cArt)
      oPB_d:disable()
   ENDIF
   //
   oPB_v := XbpPushButton():new(oDlg, ,{INT(aSize[1]-495),2},{155,26})
   oPB_v:caption  := "Žnderungen ~verwerfen"
   oPB_v:activate := {||lSave := UnwahR, nEvent := xbeP_Close}
   oPB_v:create()
   oPB_v:disable()
   //
   SetAppFocus(oMLE)
   // Event loop = Programmsteuerung
   nEvent := 0
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO
   // Text aus Editierpuffer holen und an LOCAL Variable zuweisen
   // Die Zuweisung erfolgt durch :dataLink
...  
Das Problem ist also nun gelöst.
Danke mal für die Mithilfe.
Ciao

Heinz-Dieter aus Kandel in der schönen Südpfalz
Antworten