Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!

Mobiles Hauptmenü

Während der Laufzeit in der Datenblattansicht mit VBA Spalten umbenennen

Begonnen von Doming, Februar 18, 2026, 08:59:33

⏪ vorheriges - nächstes ⏩

Doming

Hallo,

ich habe ein Formular für das Eintragen von Arbeitsstati für verschiedene Abteilungen.
Bisher hatte jede Abteilung ein eigenes Formular, in dem die für die Abteilungen relevanten Felder in der Datenblattansicht angezeigt werden.
Diese unzähligen Formulare möchte ich jetzt durch eines für alle ersetzen.
Dazu habe ich ein Hauptformular mit einer Buttonleiste (Optionsgruppe) und ein Unterformular in der Datenblattansicht.
Je nachdem, welche Abteilung (Key) angewählt wird, baut sich das Unterformular neu auf.
Es gibt eine Tabelle1 mit allen Steuerelementen und deren Bezeichnungen.
Es gibt eine Tabelle2 mit dem Layout, d.h. welches Feld aus Tabelle1 wird angezeigt, in welcher Reihenfolge und die zuletzt eingestellte Breite.
In der abfr_Layout werden die beiden Tabellen zusammengeführt, also Breite, Reihenfolge mit den Namen und Bezeichnungen.

Set rsLay = cDB.OpenRecordset("SELECT * " _
                                & "FROM abfr_Layout " _
                                & "WHERE FSForm = " & Key _
                                & " AND FSUser = " & UserID _
                                & " ORDER BY Reihenfolge")
    If rsLay.EOF Then
        MsgBox "Ich finde das Formular " & Key & " nicht"
        GoTo Ende
    Else
        rsLay.MoveLast
        SpaltZahl = rsLay.RecordCount
        rsLay.MoveFirst
        For Each ctl In Forms.frm_HaFo.RahmenFP.Controls
            If ctl.ControlType = acTextBox Then
                a = a + 1
                rsLay.FindFirst "StElement = '" & ctl.Name & "'"

   '             Debug.Print a, rsLay!Reihenfolge, rsLay!Stelement, ctl.Name, rsLay.NoMatch

                If rsLay.NoMatch _
                 Or rsLay!Breite = 0 _
                 Or rsLay!Sichtbar = 0 _
                 Or rsLay!Sichtbar = 3 _
                Then
                    ctl.ColumnHidden = True
                    ctl.ColumnOrder = SpaltZahl
                Else
                    ctl.ColumnHidden = False
                    ctl.ColumnWidth = rsLay!Breite
                    ctl.ColumnOrder = rsLay!Reihenfolge
                End If
            End If
        Next
    End If
Das funktioniert soweit.

Nun möchte ich die Spalte beim Aufbau aber auch noch umbenennen. Ein ctl.Caption = "blah" ergibt den Fehler Objekt unterstützt diese Eigenschaft oder Methode nicht


Könnt Ihr mir da weiterhelfen?

Gruß
 Doming

AHeyne

Ich denke, dass es das ist, was du benötigst:

ctl.Properties("DatasheetCaption") = "blah"

Knobbi38

Hallo Doming,

leider zeigst du nicht, wie ctl definiert ist, denn grundsätzlich funktioniert der Zugriff über die Controls-Auflistung und eine Zuweisung an die Caption Eigenschaft ebenfalls.

Wenn du tatsächlich ein Formular mit verschiedenen Ansichten darstellen möchtest, lässt sich das mit Klassen umsetzen. Du solltest vielleicht mal einen Blick auf das MVP-Entwurfsmuster (OOP) werfen.  Suche nach den Begriffen ,,Model View Presenter", dann wirst du einige Hinweise finden, da dieses Muster sehr gebräuchlich ist. Vielleicht nicht gerade in Access, aber einige Anregungen kannst du übernehmen.

Gruß Knobbi38
 


Bitsqueezer

#3
Hallo,

wenn kein "Option Explicit" verwendet wurde, ist ctl ein Variant. Wenn es deklariert wurde, sollte es als "Access.Control" deklariert werden, der generische Controltyp (die Basisklasse). IntelliSense zeigt Dir dann nur die Eigenschaften, die von allen Controls genutzt werden können. Wenn Du es expliziter und compilersicherer möchtest, kannst Du nach dem Test auf den Controltyp eine "ctl_Textbox As Access.TextBox" Deklaration verwenden und mit "Set ctl_Textbox = ctl" die spezifische Textbox-Klasse verwenden, so daß Dir alle Eigenschaften der Textbox in IntelliSense angezeigt werden und der Compiler prüfen kann, ob die Eigenschaft, die Du im Code hast, existiert. Dieser hätte Dir dann "Caption" bereits angemeckert, was er bei generischem "ctl" nicht kann, da die Eigenschaft nicht bei allen Controls existieren.

Eine Textbox hat keine Caption, nur ein Label Control.

Die DatasheetCaption ist für Datasheet-Spaltenbeschriftung, kann unabhängig von Caption gesetzt werden und kann auch direkt verwendet werden, man benötigt nicht die Properties Collection.

Statt ControlType zu testen, kannst Du auch eine If-Sonderform verwenden:
If TypeOf ctl Is Access.TextBox Then
Das ist nicht schneller oder besser, aber meist lesbarer.

Gruß

Christian

Doming

Hallo und danke für die Antworten,

der Deklarationsteil sieht so aus
Dim a As Long
 Dim frm As String
 Dim ctl As Control
 Dim ctlNm As String
 Dim cDB As DAO.Database
 Dim rsLay As DAO.Recordset

Auf Caption bin ich gekommen, weil ich beim Einlesen der Steuerelement-Tabelle in einer Schleife die Textboxen ausgelesen und über ctl.Control(0).Caption das dazugehörige Label-Element angesprochen habe.

ctl.Properties("DatasheetCaption") = "blah" werde ich nachher mal ausprobieren, vielen Dank

Gruß
Doming

Knobbi38

Hallo,

noch ein kleiner Hinweis:

eine .DatasheetCaption Eigenschaft habe ich im Objektmodell nicht gefunden, aber über die Properties-Auflistung läßt sie sich ansprechen (#1).
Es gilt jedoch zu beachten, dass eine Zuweisung an diese Property eine höhere Priorität besitzt, als eine direkte Zuweisung an die Caption Eigenschaft des zugeordneten Labels für die Spalte.

In der For Each Schleife in dem Beispielcode, werden alle Textboxen angesprochen. Für eine Textbox kann über die Textbox.Controls-Auflistung auf das zugehörige Label zugegriffen werden, was in der Datenblattansicht dem Label im Header entspricht:
ctl.Controls(0).Caption = "Spaltenüberschrift"
Knobbi38

Bitsqueezer

Hallo Ulrich,

Du hast recht, ich hatte das wohl verwechselt mit den "Datasheet..."-Properties, die im Formular selbst zu finden sind, auf die man direkt zugreifen kann.

Gruß

Christian

Doming

Moin,

mit DatasheetCaption hat es nun funktioniert, danke sehr.

den Controltyp Access.Textbox werde ich auch noch testen.

Gruß
 Doming

Doming

Hm,

Private Sub Test1
 Dim ctl As Access.TextBox

    For Each ctl In Forms.frm_Formular1
   
    Next
Exit Sub

Bringt mir schonmal den Fehler 13 - Typen unverträglich

Ich gungte davon aus, dass ich mit dem ,,For Each ctl" alle im Formular existierenden Textboxen ansprechen kann...
Da der Code aus #1 mit der DatasheetCaption jetzt funktioniert, vertage ich die Experimente mit Access.Textbox auf unbestimmt.

Gruß
Doming



Knobbi38

Hallo Doming,

ZitatFor Each ctl" alle im Formular existierenden Textboxen ansprechen kann...

Kleine Anmerkung am Rande:
du brauchst nicht über die Forms-Auflistung gehen, sondern es reicht vollkommen:
For Each ctl in Me.Controls
Du könntest jedoch mal überlegen, anstatt alle Controls zu durchlaufen, prüfen ob es eine Textbox ist und dann in der Tabelle danach zu suchen, die Logik einfach umzudrehen.

Dazu beim Öffnen alle Textboxen zunächst zu verstecken und dann einfach alle DS des Recordsets der Reihe nach zu durchlaufen und das entsprechende Control per Name anzusprechen. Das reduziert den Aufwand erheblich. Keine Suchoperationen und keine Objekt-Vergleiche usw. mehr.

Knobbi38

Doming

Hallo nochmal,

hihi, in fact sieht der Code aktuell so aus
    For Each ctl In Forms.frm_HaFo.RahmenFP.Controls
        If ctl.ControlType = acTextBox Then
            ctl.ColumnHidden = True
            ctl.ColumnWidth = 0
        End If
    Next
    Do Until rsLay.EOF
        With Forms.frm_HaFo.RahmenFP.Controls(rsLay!StElement)
            .ColumnOrder = rsLay!Reihenfolge
            If rsLay!Breite <> 0 Then .ColumnWidth = rsLay!Breite Else .ColumnWidth = -2
            .ColumnHidden = False
            .Properties("DatasheetCaption") = rsLay!Bz
        End With
        rsLay.MoveNext
    Loop
Weil mir diese Controlsucherei in einer Schleife auch zu zeitintensiv war...

Me.Controls habe ich nicht genommen, weil ich vom Hauptformular aus aufrufe.

Danke fürs Gedankenmachen

Gruß
 Doming

Knobbi38

Hi,

dann hast du ja schon alles umgesetzt,  :)
allerdings hätte ich dann doch eine Referenz auf die Controls-Auflistung ausserhalb der Schleifen erstellt, anstatt diese bei jedem Schleifendurchlauf neu aufzulösen, und das gleich in zwei Schleifen.

Zitat von: Doming am Februar 19, 2026, 12:33:30Me.Controls habe ich nicht genommen, weil ich vom Hauptformular aus aufrufe.
Eigentlich gehört der Code in das Unterformular, weil das HF nichts über die Controls im UF wissen muss. So etwas fällt dann unter den Begriff "Datenkapselung", was schon mal als "Good Practice" gilt. Wenn du das aus dem HF dann anstoßen möchtest, würde man im UF eine "Public Methode" anlegen, z.B. InitControls und die würde das dann organisieren - kein Grund, Interna aus dem UF offen zu legen. Damit wäre dann die Forderung nach einer "Datenkapselung" wieder komplett erfüllt. Vorteile ergeben sich dann automatisch bei Refactoring und es würde auch erlauben, dieses UF dann in einem anderen HF einzubinden, z.B. zu Testzwecken oder als Vorbereitung für eine neue Version.

Knobbi38

Bitsqueezer

Hallo,

Dim ctl As Access.TextBox

    For Each ctl In Forms.frm_Formular1

hier setzt Du voraus, daß For Each in ctl nur Textboxen lädt, nur weil Du ctl als Textbox-Objekt deklariert hast.

Eine Filterung geschieht hier nicht. For Each geht immer alle Objekte aus der Auflistung durch, die Du ansprichst, deswegen heißt es ja auch "Each" und nicht "Each Textbox".

Du hast eine Auflistung "Controls", da stehen halt alle Control-Arten drin, die im Formular sind. Entsprechend muss "ctl" mit "Access.Control" deklariert werden, also das generische Basisobjekt, auf dem alle Control-Objekte beruhen.

Du kannst also schreiben:
    Dim ctl As Access.Control
    Dim ctl_TB As Access.TextBox
   
    For Each ctl In Me
        If TypeOf ctl Is Access.TextBox Then
            Set ctl_TB = ctl
            'ab hier ist in ctl_TB in IntelliSense alles, was zu einer Textbox gehört,
            ' während in ctl nur das ist, was alle Controls gemeinsam haben.
        End If
    Next

Man kann es also noch weiter kürzen "In Me" genügt auch.

Gruß

Christian