Ja, es gibt auch Situationen in denen man nicht nur immer von Desktop auf Web trimmt, sondern auch mal den umgekehrten Weg einschlägt. Der folgende Beitrag nimmt den HTML-Tag fieldset als Grundlage und wir erstellen eine Shape-Klasse mit vergleichbarer Funktionalität. Das HTML-Element fieldset stellt einen Rahmen mit optionaler Überschrift dar. Mit den Controls Shape und Label (optional noch Container) können wir das ebenfalls in VFP realisieren. Hier zunächst mal der HTML-Code für ein fieldset:

<fieldset id="Restore">
<legend>Datenbank wiederherstellen</legend>
<p>...</p>
</fieldset>

Das Element legend ist für die Beschriftung verantwortlich und repräsentiert unsere Labelklasse.

Shape - die spartanische Variante

Der erste Lösungsansatz ist sehr einfach gehalten: Wir erzeugen uns eine Shape-Klasse, die eigenständig ein zugehöriges Label instanziiert und positioniert:

*======================================================================
* Fieldset-Shape
* Erzeugt bei der Initialisierung ein Label zur Beschriftung des Shapes
* und positioniert es.
*======================================================================
Define Class FieldSet As Shape
 cCaption = ""
 cPrefixLabel = "lbl"
 nOffsetTop = -7
 nOffsetLeft = 5
 lUseLabel = .T.  && Readonly @ Runtime ;-)
 MemberClass = ""
 MemberClassLibrary = ""
 Protected oLegend
 oLegend = .Null.
 
 *------------------------------------------------------------------
 * Initialisierung und (optionale) Erstellung der Beschriftung.
 *------------------------------------------------------------------
 Function Init() As Boolean
  DoDefault()
  If This.lUseLabel
   This.createLabel()
  EndIf
 EndFunc
 
 *------------------------------------------------------------------
 * Label instanziieren und positionieren.
 *------------------------------------------------------------------
 Protected Function createLabel() As VOID
  With This
   If Vartype(.cCaption) == "C"
    .oLegend = NewObject(Evl(.MemberClass, "Label"), .MemberClassLibrary)
    If Vartype(.oLegend) == "O"
     With .oLegend As Label
      .Name = This.cPrefixLabel + This.Name
      .BackColor = Thisform.BackColor
      .BackStyle = 1
      .Anchor = 0
      .Left = This.Left + This.nOffsetLeft
      .Top = This.Top + This.nOffsetTop
      .Anchor = Bitclear(Bitclear(This.Anchor, 2), 3)
      .ZOrder(0)
     EndWith
     This.cCaption = This.cCaption
    EndIf
   EndIf
  EndWith
 EndFunc

 *------------------------------------------------------------------
 * Nachträgliche Änderungen der Caption gestatten.
 * Dies ermöglicht auch das Aus-/Einblenden des Controls.
 *------------------------------------------------------------------
 Function cCaption_Assign(tcValue As String)
  With This
   If Vartype(.oLegend) == "O"
    .oLegend.Caption = m.tcValue
    .oLegend.Visible = Not Empty(m.tcValue)
   EndIf
   .cCaption = m.tcValue
  EndWith
 EndFunc
 
 *------------------------------------------------------------------
 * Schreibschutz für This.lUseLabel implementieren.
 *------------------------------------------------------------------
 Function lUseLabel_Assign(tlValue As Boolean)
  Error 1740, "lUseLabel"
 EndFunc
 
 *------------------------------------------------------------------
 * Objektreferenz sauber auflösen und Label entsorgen.
 *------------------------------------------------------------------
 Function Destroy() As Boolean
  With This
   If Vartype(.oLegend) == "O"
    .oLegend = .Null.
   EndIf
  EndWith
 EndFunc
EndDefine

Der gezeigte VFP-Quellcode dürfte weitestgehend selbsterklärend sein. Wenn in der Designzeit dem Shape die Caption gestattet wird, so wird zur Laufzeit ein Labelobjekt aus der angegebenen Klasse erzeugt und als Referenz gespeichert.

Das Label erhält den gleichen Namen wie das Shape jedoch mit frei wählbarem Prefix, da wir eindeutige Objektnamen benötigen. Sofern bei der Initialisierung noch keine Beschriftung angegeben sein sollte, wird das Label noch nicht angezeigt. Die Caption kann zu jedem beliebigen späteren Zeitpunkt gesetzt (und damit eingeblendet) und wieder ausgeblendet werden.

Zur Vermeidung von hängenden Referenzen und daraus resultierenden C0005-Fehlern (samt Komplettabsturz) tragen wir dafür Sorge, dass das Label ebenfalls sauber bei der Zerstörung des Shapes entfernt wird.

Wichtig ist hierbei ebenfalls, dass wir die Eigenschaft lUseLabel zur Laufzeit mit einem Schreibschutz versehen. Schliesslich soll der Entwickler sich bereits in der Entwicklungsumgebung entscheiden, ob eine Beschriftung erforderlich sein könnte oder nicht.

Bis denne, JoKi

PS: Die fehlenden Assign-Methoden für weitere Eigenschaften sind Hausaufgabe... 😁