Klassenkampf Die Powersoft Foundation Classes Dr.-Ing. Andreas Böttcher, Dipl.-Inform. Carsten Müncheberg Triagon Software GmbH Die Powersoft Foundation Classes als Basis für Branchensoftware Vorwort Vermittlung praktischer Erfahrungen kein Anspruch auf Vollständigkeit 2 Übersicht Grundlagen des Entscheidungsprozesses zur PFC Thesen konkrete Beispiele zu den Services 3 Projektsoftware vs. Branchensoftware Projekt - Lösungen – spezielle Wünsche und Traditionen der Anwender – vorgegebene Umgebung – Rapid Application Developement 4 Branchensoftware (1) Allgemeine Lösung für verschiedene Firmen der Branche Ähnlichkeiten: Datenmodell, Semantik Unterschiede: Gestaltung, Handhabung, Schwerpunkte, Zusätze Zentrale Unternehmensanwendung 5 Branchensoftware (2) Anpassung Modularisierung – Erweiterungen auch lange nach Erstinstallation Flexible Wartung – durch die Wartungsabteilung statt der Entwickler 6 PowerBuilder und die PFC Unterstützt PB mit PFC die Anforderungen von Branchensoftware? – hervorragendes Reporting – sehr gute SQL Einbettung in PowerScript – konsequente Objektorientierung in PowerScript und PFC – PFC beste Klassenbibliothek bei 4GL 7 Einsatz von PB und PFC Wer macht – Projektsoftware – Branchensoftware Wer arbeitet mit PowerBuilder – pur – mit den PFC – mit einer anderen (eigenen) Library 8 Triagon Software GmbH Gegründet 1990 Clipper-Software für Verlagsadministration 150 Installationen, ca. 800 AP Neu: Client-Server-Appl. mit PB 30-100 AP / Installation 2 Entwickler, 2 Support/Hotline, 2 Vertrieb, 2 Admins Entwickler: 2 MJ Vorerfahrung mit PB 9 Thesen 1. Verwende von Beginn an eine Klassenbibliothek! 2. PowerBuilder mit PFC ist für Branchensoftware geeignet. 3. Neben der PFC benötigt man keine weiteren Frameworks. 4. Sei bereit, der PFC bis in die Abgründe ihres Codes zu folgen! 5. Man braucht nicht unbedingt einen zusätzlichen Enterprise- oder Application-Layer. 10 These 1 Verwende von Beginn an eine Klassenbibliothek. – Schnelle funktionsfähige Prototypen mit PB – Aber ohne Framework nur Kleinapplikationen – Eigenentwicklung einer Library nicht nötig. Erweitern sie ggfs. die PFC. – Es geht nicht nachträglich! 11 These 1 (Forts.) Höherer Aufwand - höherer Nutzen Erhöhte Einarbeitung in die PFC Nutzen: vorgefertigte sinnvolle Standardfunktionalität Hohe Quality Assurance durch viele Nutzer Dokumentation Offene Quellen und Erweiterbarkeit Vorgabe externer neutraler Standards 12 These 2 Auch Branchensoftware ist mit PFC machbar PFC ist Framework, kein Application Generation Tool – viele Services (SBA) – aber keine vorgefertigten funktionalen Fenster 13 These 3 Du sollst keine anderen Libraries neben der PFC haben PFC hat fast alle originären Konzepte anderer Libraries aufgesogen. Hochqualifizierte Entwickler und Support Open Source Vorteile: – Quellcode ist frei – weite Verbreitung 14 These 3. (Forts.) Aufsetzende Libraries Nachteil • zusätzliche Vererbungschicht aller Objekte Vorteil • enthalten meist Objekte der Application-Schicht Low-Level Libraries multi-purpose function library FUNCKy 15 These 4 Folge der PFC in die Abgründe ihres Codes Einige Unzulänglichkeiten / Fehler Zum Verständnis den Code befragen Erfordert qualifizierte Entwickler 16 These 5 Nicht mehr Layer als unbedingt erforderlich erzeugen Enterprise Layer – sorgt für einheitliche Gestaltung aller Applikationen der Firma – eigene allgemeine Basisobjekte Application Layer – definiert die funktionalen Basisobjekte der Anwendung Performance / Größe / Übersicht 17 Arbeiten mit PB & PFC Exzellente Tools für RAD und Prototyping Intime Kenntnisse der PFC für größere Projekte notwendig Hoher Lerneffekt Definition von Standards Mehr Probleme im PowerBuilder als in PFC 18 Arbeiten mit PB & PFC PFC - Dokumentation – – – – (2) Kontextsensitive Hilfe Analyse des Codes oft notwendig Kommentare dürftig Header ärgerlich Tips: – Scripts nicht länger als eine Seite machen – eigenen Header definieren 19 Arbeiten mit PB & PFC (3) Lokalisierung – deutsche Version mit Verzug – neueste Version: engl. Texte z.T. hart codiert – Änderungen im Extension-Layer Starte mit PFC 6.0 PFC online: www.pfcguide.com www.pfccheatsheet.com news://powersoft.public.powerbuilder.pfc/ 20 PFC - Services & Objekte SBA: Service Based Architectures www.concentric.net/~Bgcastle/PAPERS/SBA/SBA_Download.shtml Application Manager – kapselt den Startup – hält globale Variablen Vererbung ist möglich Message Router – Verständnis ist notwendig – dynamischer Aufruf von Events im fokussierten Objekt aus dem Menü – window, fokussiertes Control, zuletzt aktives DW 21 Error Service Von Beginn an benutzen: – gnv_app.inv_error.of_message(...) Erweiterungen: – Parameter Substitution: %1, %2, ... Vorher nachher – Meldungen als E-Mail durch Nutzung des MailSend Fensters der Examples Probleme: – in den PFC keine konsequente Verwendung des ErrorService – Übersetzung des Services 22 Property Service Services dynamisch Ein-/Ausschalten Erzeugung der Syntax Einschalten der Services direkt aus dem DW – kein Scripteingriff notwendig – DW-Design und Funktionalität gemeinsam – Datakanja de Bruyn (PB User Meeting 9/98) – Beispiel: String lsRowSelect lsRowSelect = Describe( "st_rowselect.Text" ) CHOOSE CASE lsRowSelect CASE '0', '1', '2' ; of_setRowSelect(TRUE) inv_rowSelection.of_setStyle(integer(lsRowSelect)) CASE '!' ; // nicht vorhanden CASE ELSE ; // Fehlerbehandlung END CHOOSE 23 SQL Spy Service Debugging und Optimierung der SQLs – in Basisklassen berücksichtigt (u_dw) Erweiterung: – Ausführungsplan (Sybase SQL Anywhere) – Beispiel: 24 Menüs: Anforderungen Vor dem Entwurf der Fenster grundlegende Menüstruktur festlegen Besondere Anforderungen von Branchensoftware: – Nicht alle Kunden/Benutzer haben die gleiche Funktionalität (Module) lizensiert. – Spezielle Funktionalität darf nur für Administratoren zugänglich sein. Möglichst flexible Menüstruktur notwendig 25 Menüs: Welche Ansätze gibt es? MDI mit Frame- und Sheetmenüs (PFC-Standard) MDI mit einzelnem Hauptmenü und mehreren Kontextmenüs (Triagon) Explorer-Interface (z.B. MS-Outlook und de Bruyn) 26 Menüs: Stärken und Schwächen komfortabler Zugang zu den Services über den Message Router Service ineffizient implementiert, daher wenige Vererbungseben und wenige dynamische Modifikationen möglich Menüpunkte werden nicht disabled, wenn Services abgeschaltet sind 27 MDI mit Frame- und Sheetmenus “Two-Tier approach” – Master-Menü erzeugen Kopieren von pfc_m_master nach m_xxx_master Nicht benötigte Menüpunkte löschen Eigene Menüpunkte hinzufügen – Frame-Menü erzeugen m_xxx_frame ableiten von m_xxx_master Einzelne Menüpunkte auf disable/invisible setzen – Sheet-Menüs erzeugen m_xxx_sheet_... ableiten von m_xxx_master Einzelne Menüpunkte auf disable/invisible setzen Spezielles “Action”-Menü pro Sheet hinzufügen Nachteil: Nicht wiederverwendbar 28 MDI mit Haupt- und Kontextmenüs Alle Fenster verwenden das gleiche Hauptmenü – Master-Menü erzeugen Kopieren von Pfc_m_master nach m_xxx_master enthält nur Standard-Menüpunkte – Hauptmenü-Menü erzeugen m_xxx_main ableiten von m_xxx_master enthält applikationsspezifische Menüpunkte Menüpunkte, die nur einzelne Sheets oder DW betreffen, werden in Kontextmenüs verbannt. 29 Kontextmenüs In pfc_u_dw ist m_dw fest eingestellt, kann geändert werden durch Überschreiben von event rbuttonup // Create popup menu // lm_dw = create m_dw (ersetzt durch) lm_dw = create USING is_RMB_Menu Nachteil: Keine Shortcuts, daher unergonomisch bei Tastaturbenutzung Windows-Standards: – Shift+F10 aktiviert Kontextmenüs – Wie bindet man die Win95-Menütaste ein? 30 Explorer-Interface Ersetzt MDI-Frame Der TreeView (Pfc_u_Tv) eignet sich zum Anwählen der einzelnen “Sheets”, die alle in einen Explorer-Fenster integriert sind. Datenbankgestützter Aufbau des TreeViews sehr einfach möglich, dadurch wird wird die geforderte Konfigugrierbarkeit erreicht. Da ein Teil der Navigation über den TreeView erfolgt, vereinfacht sich der Aufbau der behäbigen Menüs. 31 Resize Service Der Resize Service verschiebt oder skaliert alle Controls in einem Window oder auf einer Tab-Page individuell entsprechend ihrer Registrierung: – FixedToRight, FixedToBottom, FixedToRight&Bottom, Scale, ScaleToRight, ScaleToBottom, ScaleToRight&Bottom, FixedToRight&ScaleToBottom, FixedToBottom&ScaleToRight Zusammen mit dem Preference Service sehr gut für Anpassung der Applikation an unterschiedliche Bildschirmauflösungen 32 Preference Service Speichert Göße und Position eines Fensters sowie Menü- und Toolbar-Einstellungen in der Registry oder in einem INI-File. Standard-Einstellungen sind nicht sinnvoll, deshalb: inv_preference.of_SetMenuItems(FALSE) inv_preference.of_SetToolbarItemOrder(FALSE) inv_preference.of_SetToolbarItemSpace(FALSE) inv_preference.of_SetToolbarItemVisible(FALSE) inv_preference.of_SetToolbarTitles(FALSE) 33 Preference Service (Forts.) Bug: Maximized! Window-State wird nicht beachtet (behoben in PFC 7.0) Erweiterung: Einstellungen nicht automatisch im Close-Event sondern nur auf Anforderung speichern – CloseEvent überschreiben: Of_SetPreference(FALSE) call super::close – UserEvent: pfe_prefsave inv_preference.Of_Save() Wunsch: Speichern der Splitbar-Einstellungen 34 Logical Unit of Work Service Event pfc_endtran() Achtung: Commit / Rollback nur in Quickstart (qckmain.pbl) implementiert Event pfc_save – Zu langsam bei Fenstern mit vielen Controls Lösung: – event pfc_saveobjects(apo_control[]) oder – of_SetUpdateObjects(apo_control[]) und dann event pfc_save aufrufen 35 Logical Unit of Work Service (Forts.) Speichervorgang tunen – Im event editchanged eine Referenz auf das Control in einem Array ablegen – Zum Speichern event Pfc_saveobjects aufrufen und danach das Array rurücksetzen Automatisches Speichern in event rowfocuschanging einbaubar 36 Multi Table Update Service hilfreich bei der Erstellung von DW für die effiziente Erfassung von komplexen Datenstrukturen Beispiel: Komplette Eingabe von Firma – Ansprechpartner – Anschrift in einem DW. Vorteile: – Der Anwender muß sich bei der Eingabe nicht durch mehrere Tab-Pages hangeln – Die Eingaben sind vor dem Speichern vom Anwender in ihrer Gesamtheit besser überprüfbar – Im Falle eines DB-Fehlers kann der Anwender seine Eingaben sofort korrigieren, eine komplizierte Routine zum Anspringen der „fehlerhaften“ Tab-Page ist nicht notwendig 37 Multi Table Update Service (Forts.) Problem: – Der Service bietet keine keine Unterstützung für die Identity-Column. – Identity-Columns werden benötigt, wenn man mit AutoIncrement (SQL-Anywhere) arbeitet. Beispiel: INSERT SELECT INSERT SELECT INSERT INTO FIRMA ... FIRM_KEY FROM FIRMA ( 1. Identity-Column) INTO PARTNER SET FIRM_KEY= .... PARTNER_KEY FROM PARTNER ( 2. Identity-Column) INTO ANSCHRIFT SET PARTNER_KEY = ... 38 Linkage Service Styles: – Retrieve – Filter – Synchronized Scrolling Probleme: – Tab-Folder: Nur die DW auf der angewählten Tab-Page sollen synchronisiert werden – Keine AutoIncrement-Unterstützung – Shared DW werden nicht unterstützt 39 Selection Service (n_cst_selection) Erweiterungen – Retrieve As Needed – WHERE-Clause übergeben statt Retrieval-Argument – Resizeable Response-Window: long ll_styles IF IsValid(inv_resize) THEN ll_styles = GetWindowLongA(Handle(this), -16) IF ll_styles <> 0 THEN SetWindowLongA(Handle(this), -16, ll_styles + & 262144/*WS_THICKFRAME*/ /*+ 524288 WS_SYSMENU*/) END IF END IF 40 Selection Service (Forts.) FUNCTION long GetWindowLongA(ulong whand, int index) LIBRARY "user32.dll" FUNCTION long SetWindowLongA(ulong whand, int index, ulong styles) LIBRARY "user32.dll" 41 Fallen und Tricks SaveAs mit Sonderzeichen bei .csv Find Service: ‘&‘ nicht gefiltert Calendar Service – Beispiel: 42 Fallen und Tricks (2) Sort Service: Header-Sort vs. Column Move u_dw.EVENT clicked() // AB: Header-ColumnMoving anschalten IF KeyDown( KeyControl!) and row = 0 THEN IF Pos( GetBandAtPointer(), 'header~t') = 1 THEN & Object.Datawindow.Grid.ColumnMove = 'yes' u_dw.EVENT lbuttoup() // AB: Header-ColumnMoving wieder ausschalten, wenn es an ist. IF Pos( GetBandAtPointer(), 'header~t') = 1 THEN Object.Datawindow.Grid.ColumnMove = 'no' END IF 43 Fallen und Tricks (3) Sort Service: aktuelle Zeile festhalten n_cst_dwsrv_sort.FUNCTION of_sort() ll_row = idw_Requestor.GetRow() IF ll_row > 0 THEN ll_rowid = idw_Requestor.GetRowIdFromRow(ll_row) li_rc = idw_Requestor.Sort() IF ll_rowid > 0 THEN idw_Requestor.ScrollToRow(idw_Requestor.GetRowFromRowId(ll_rowid)) IF idw_requestor.GetRow() <> idw_requestor.GetSelectedRow(0) THEN idw_requestor.event rowfocuschanged(idw_requestor.GetRow()) END IF 44 Tips long varchar & timestamps beim Update – trotz UpdateWhere= ‘Key and Modified‘ werden diese nicht in das WHERE übernommen Lösung: – im SqlPreview-Event weitere Columns an das WHERE anfügen 45 Tips (2) ODBC: SQL-Binding – disableBind=1 – Werte im SQL enthalten (sonst nur ‘?‘) sichtbar im SQL-SPY Vorsicht: bei DisableBind=0 sind immer ALLE updateable Columns im UPDATE .. SET column=?, ... enthalten! 46 Tips (3) AncestorReturnValue – Extended Events müssen immer den AncestorReturnValue zurückgeben, auch wenn nur Kommentar im Skript! Kein PageUp/PageDown in multiline columns – mle-columns statt mle-controls vorteilhaft wg. DataSharing Lösung: pbm_dwnKey abfangen, Positionierungstasten behandeln 47 Utilities PFC String-Klassen – besser: Datawindows/Datastores nutzen FUNCKy Library – fast unersetzlich (string Funktionen) – PB-Interface mit kleinen Fehlern – Verwendete Funktionen 48 Zusammenfassung PFC verwenden! Erfahrungen sammeln! – Frontend-Services sind einfach – Backend-Services sind komplexer 49 50 51 52 string functions token(ref string s,ref string d,int o,ref string b) int numtoken(string s,string d) strtran(ref string s,ref string o,ref string n,int i,int c,ref string b) strtrani(ref string s,ref string o,ref string n,int i,int c,ref string b) uint attoken(string s,string d,int o) chrswap(ref string s,ref string c1,ref string c2,ulong b) replicate(ref string s,int c,ref string b) strextract(ref string s,ref string d,int c,ref string b Datetime functions fsplit(ref string f,ref string d,ref string p,ref string n,ref string e) fgetdatef(ref string f,ref string f,ref string b) fgettime(ref string f,ref string b) Directory und File functions findfirst(ref blob d,ref string f,int a,ref string b findnext(ref blob d,ref string b) curdir(ref string d,ref string b boolean chdir(string d) boolean setdrive(string d boolean isfile(string p,int a) Utility - Functions getenv(ref string n,ref string b) encrypt(ref string s,ref string k,ulong b) decrypt(ref string s,ref string k,ulong b) long GetWindowLongA(ulong whand, int index long SetWindowLongA(ulong whand, int index, ulong styles 53