Datenbank-Entwicklung mit mehr Komfort – Teil III

Die nächste Fahrt geht rückwärts!

Sie sind Teil I und Teil II gefolgt und haben noch nicht genug von den Verstrickungen in Interfaces und Datenbankobjekten? Dann verdienen Sie es auch, jetzt ein Textfield zu bekommen, das seinen neuen Wert in die Datenbank zurückschreibt.

Gutes Timing ist alles

Aber zunächst mal: Wann soll das überhaupt passieren? Theoretisch sind zwei Möglichkeiten denkbar: Ein sofortiges Update nach jeder Textänderung – also via TextField.TextChange-Event –, und ein Update, wenn der Benutzer ein anderes Steuerelement annavigiert – dann wäre TextField.LostFocus der Event der Wahl.

Vorteil Methode 1: Wir würden keine Aktualisierung vergessen. Die Daten würden ja bei jeder Änderung geschrieben werden.

Nachteil Methode 1: Die Daten würden bei jeder Aktualisierung geschrieben werden. Verursacht auf jeden Fall mehr SQL-Traffic. In einer Mehrbenutzerumgebung via Web mag sich das bald störend auswirken, zumal ja angeschlossene Controls auch immer gleich aktualisiert werden sollten.

Vorteil Methode 2: Nur ein Schreibvorgang, wenn die Bearbeitung auch wirklich abgeschlossen wurde.

Nachteil Methode 2: Vor dem Wechsel des Recordsets der steuernden Tabelle sollte ein ClearFocus ausgelöst werden, damit keine Aktualisierung vergessen wird (falls der Cursor nämlich noch im TextField stehen sollte).

Können Sie hier einen eindeutigen Favoriten ausmachen? Ich nicht. Ach, und außerdem gäbe es mindestens noch eine Methode 3: Eine Änderung eines TextFields setzt einfach das Field auf Dirty – benutzt eine Boolesche Variable, um zu markieren, dass es verändert wurde –, und bevor sich die Tabelle ändert, prüft sie ihre Felder auf Schmutzigkeit und löst ein Update aus. Man könnte hier, also Methode 4, auch einen Button dazwischen klemmen, um eine manuelle Bestätigung des Updates zu erzwingen. Aber sowas – wenn überhaupt – einmal später. Halten wir’s erst mal (möglichst) einfach – das Thema ist ja verworren genug. Und eines Kniefalls würdig, eben gerade wenn man sich den Komfort von FileMaker anschaut. Da ist das alles (und viel mehr) auch noch in eine grafisch bedienbare Oberfläche eingegossen.

Ich kann keine Lösung präferieren, will aber auch nicht, dass ein späterer Benutzer der Klassen (oder ich selbst) vergisst, mindestens eine davon zu implementieren. Ergo:

  • Das Modul EaSyQL bekommt eine Enumeration EaSyQLFieldUpdateStyle mit den Werten OnTextChange und OnLostFocus.
  • Die Klasse EaSyQLTextField erhält eine Property
Public Property UpdateStyle as eaSyQL.EaSyQLFieldUpdateStyle = eaSyQL.EaSyQLFieldUpdateStyle.onTextChange

und einen Eventhandler

Sub TextChange() Handles TextChange
 if not ignoreTextChange then
  if not RaiseEvent TextChange and me.UpdateStyle = easyql.EaSyQLFieldUpdateStyle.OnTextChange then 
  UpdateFieldValue
  end if
 end if
End Sub

Der duplizierte Event TextChange bekommt jetzt noch einen Boolean als Rückgabewert dazu. Erhält eine Instanz von EaSyQLTextField einen TextChange-Handler und liefert darin True zurück, wird das automatische Update nicht ausgelöst.

Ich gebe EaSyQLTextField noch eine neue Property

Public Property Locale as Xojo.core.Locale

Und weise ihr im Open-Event durch Einfügen an erster Stelle einen Vorgabewert zu:

 Locale = xojo.core.locale.Current

Dann ist die Ausgabe besser individualisierbar. Ähnlich werden wir demnächst die Eingabe flexibilisieren.

Dann der Hauptverantwortliche:

Public Sub UpdateFieldValue()
 dim newtext as text = me.text.ToText
 dim doublevalue as double = double.Parse(newtext, locale)
 if me.field <> nil and newtext <> lastvalue then
  select case me.Field.Type
  case eaSyQLField.eaSyQLFieldType.BooleanType
    me.Field.BooleanValue = if (newtext = eaSyql.kTrue, True, False)
  case eaSyQLField.eaSyQLFieldType.CurrencyType
   dim cur as Currency = doublevalue
   me.Field.CurrencyValue = cur
  case eaSyQLField.eaSyQLFieldType.DateType
   try
    dim d as xojo.core.date = xojo.core.date.FromText(newtext, locale)
    me.Field.DateValue =d.todate
   catch
    me.field.DateValue = nil
   end try
  case eaSyQLField.eaSyQLFieldType.DoubleType
   me.Field.DoubleValue = doublevalue
  case eaSyQLField.eaSyQLFieldType.Int64Type
   me.Field.Int64Value = int64.Parse(newtext)
  case eaSyQLField.eaSyQLFieldType.IntegerType
   me.Field.IntegerValue = integer.parse(newtext)
  case eaSyQLField.eaSyQLFieldType.TextType
   me.Field.StringValue =newtext
  case eaSyQLField.eaSyQLFieldType.BlobType
   RaiseEvent SaveBlob
  case eaSyQLField.eaSyQLFieldType.unknown
   RaiseEvent SaveUnknown
  end select
 end if
End Sub

Das ist eigentlich die TextValue-Methode von EaSyQLField rückwärts. Aus dem Text des TextFields wird wieder ein dem Feldtyp entsprechender Datentyp erzeugt und der entsprechenden Interface-Methode zugewiesen (dazu gleich mehr). Vorher erhält eaSyQLTextField aber noch zwei weitere Events ohne Parameter:

Event SaveBlob()
Event SaveUnknown()

Da ein Blob (und ggf. ein umtypisierter Wert) eine individuelle Bearbeitung benötigt (muss ich ein Bild umwandeln? Einen Klang? Irgendetwas ganz anderes?), soll auch Platz für das persönliche Handling sein. Der wäre dann in den Eventhandlern zu suchen.

Und nun zum Schreiben selbst, nämlich in der Klasse

EaSyQLField

Die Interface-Methoden für die Getter (BooleanValue etc.) haben wir ja bereits gefüllt, aber die virtuellen Setter, also die Value-Methoden mit Assigns in der Parameterlist noch nicht.

Assigns?

Das gehört auch zu den virtuellen Süßigkeiten. Mit Assigns kann einer Methode ein Wert per Zuweisung “ = “ übergeben werden. Bei

Public Sub BooleanValue(assigns value As Boolean)

erfolgt der Aufruf also mittels meinField.BooleanValue = True – ganz so, als hätten wir eine Property vor uns.

Vollständig bekommt dieser BooleanValue-„Setter“ den Code

Public Sub BooleanValue(assigns value As Boolean)
 // Part of the DatabaseFieldInterface interface.
 if me.table.RecordCount > 0 then 
  me.table.Edit
  me.table.RS.Field(me.FieldName).BooleanValue = value
  me.table.Update
  me.table.ReloadQuery
 end if
End Sub

EaSyQLTable.RecordCount, zur Erinnerung, überprüft den Record erst auf Nil und liefert bei Existenz die Anzahl der Records, sonst 0. Das war besagte Tipp-Abkkürzung.

Edit und Update der Table-Klasse prüfen immer auf Datenbankfehler. Also auch hier eine Abkürzung, und ggf. ein Ort, um Fehlerhandling via Try/Catch zu installieren.

In der Mitte wird dann einfach der neue Wert geschrieben, und ReloadQuery folgt gleich. Zunächst:

  • Deklinieren Sie die anderen Setter-Methoden von EasyQLField analog durch, also wie
Public Sub CurrencyValue(assigns value As Currency)
 // Part of the DatabaseFieldInterface interface.
 if me.table.RecordCount > 0 then 
  me.table.Edit
  me.table.RS.Field(me.FieldName).CurrencyValue = value
  me.table.Update
  me.table.ReloadQuery
 end if
End Sub

usw. TextValue ist einfach

Public Sub TextValue(Assigns value as text)
 me.StringValue = value
End Sub

Dann noch die versprochene Erweiterung für

EaSyQLTable

Bislang kennt die ja nur eine Datensatz-Aufrufmethode, nämlich SelectAll. Dort hatten wir die SQLQuery in der Property Query hinterlegt. Wir können sie daher einfach erneut aufrufen und der Property RS zuweisen. Vorher allerdings blättern wir im temporär erzeugten Recordset rs bis zur Position weiter, an der sich die Property RS zuvor aufgehalten hatte – sofern der neue Recordset mindestens RSPosition viele Records besitzt. Würden wir das nicht machen, würden die TextFields wieder den ersten Datensatz anzeigen, selbst wenn schon an andere Position weitergeblättert worden war.

Public Sub ReloadQuery()
 dim pos as integer = RSPosition -1
 dim rs as recordset = me.DataBase.SQLSelect(Query)
 for q as integer = 0 to min(pos, rs.RecordCount-1)
  rs.MoveNext
 next
 me.rs = rs
 RSPosition = pos+1
End Sub

Nun noch den fehlenden anderen Event der EaSyQLTextField-Klasse verpassen:

Sub LostFocus() Handles LostFocus
 if not RaiseEvent LostFocus and me.UpdateStyle = easyql.EaSyQLFieldUpdateStyle.OnLostFocus then 
  UpdateFieldValue
 end if
End Sub

Den Event selbst duplizieren und auch mit einem Booleschen Rückgabewert ausstatten, und dann am besten noch die Property UpdateStyle der TextField-Klasse im Inspector sichtbar machen:

UpdateStyle.png

… und wir sind ein ganzes Stück weiter. Das hier angehängte Demoprojekt lässt Sie die Namen der Kunden verändern: Der Vorname wird bei jeder Änderung neu geschrieben, der Nachname nur nach LostFocus. Blättern Sie also weiter, während der Cursor im Nachnamen-Feld steht, wird der neue Name nicht übernommen. Um Komfort dieser Art kümmern wir uns im nächsten Artikel der Reihe…

 

 

Ein Gedanke zu “Datenbank-Entwicklung mit mehr Komfort – Teil III

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s