Translate

Montag, 19. Dezember 2016

ACCESS: Prüfen ob Formularkopf/-fuß existiert. Wie viele Sections liegen vor?

Wie kann mit VBA festgestellt werden, ob ein Formular/Report einen gewissen Bereich (z.B. Formularkopf) enthält?
Bei der Programmierung innerhalb eines Formulars, stellt sich nicht wirklich die Frage, den es ist ja bekannt was im aktuellen Formular an Bereichen eingerichtet ist und was nicht.

Bei der Programmierung einer allgemeinen Formularübergreifenden Prozedur, ist der Prozedur dies jedoch nicht bekannt, und da stellt sich sehr wohl die Frage, ob eine gewisse Section (Bereich) vorhanden ist oder nicht.
Dazu müssten wir eigentlich nur die VBA Auflistung Section befragen.
Doch Leider gibt es nicht solch eine Auflistung für dieses Objekt :-(

Also schreiben wir uns einfach eine Globale-Funktion die diesen Job für uns erledigt:
Public Function SectionExists(FormReportOBJ As Object, SectionIndex As AcSection _
                    , Optional ByRef SectionOBJ As Section) As Boolean
    On Error Resume Next
    Set SectionOBJ = FormReportOBJ.Section(SectionIndex)
    SectionExists = (Err.Number = 0)
End Function

Hinweis: Das Objekt Section stellt uns leider nicht die Information zur Verfügung, wie viel Sections vorhanden sind. Somit bleibt uns nichts anderes übrig, als dies mit der Fehlerabfangmethode zu lösen. Nicht elegant, aber es funktioniert ;-)
Andere Fehler haben wir nicht zu erwarten, so dass hier keine weitere Fehlerbehandlung benötigt wird.

Und so kann jetzt unsere Funktion angewandt werden:
Private Sub Form_Load()
    Dim SectionOBJ As Section
   
    If SectionExists(Me, acHeader, SectionOBJ) Then

         SectionOBJ.BackColor = RGB(255, 0, 0) 'Farbe Rot
    End If
End Sub

Erläuterung: Bei Laden des Formulars wird geprüft ob das aktuelle Formular (ME) einen Formularkopf enthält. Wenn dies der Fall ist, liefert unsere Prüf-Funktion SectionExists() den Wert True zurück und die abgefragte Section wird mit der Hintergrundfarbe Rot versehen. Die Variable SectionOBJ ist übrigens per Referenz-Übergabe in der Funktionsprozedur SectionExists() gesetzt worden.

Wenn wir schon dabei sind, mit der gleichen Technik schreiben wir uns noch eine Funktion, die uns die Anzahl der vorhandenen Sections zurück liefert:
Public Function SectionsCount(FormReportOBJ As Object) As Byte
    Dim i As Byte, SectionOBJ As Section
   
    On Error Resume Next
    For i = 0 To 255
        Set SectionOBJ = FormReportOBJ.Section(i)
        If Not Err.Number = 0 Then Exit For
    Next
    SectionsCount = i
End Function


Dies kann z.B. interessant sein, wenn per VBA alle Sections im Formular mit einer Farbe versehen werden soll. Und so geht's:
Private Sub Form_Load()
    Dim i As Byte
  
    For i = 0 To SectionsCount(Me)-1
        me.Section(i).BackColor = RGB(255, 0, 0) 'Farbe Rot
    Next
End Function


Das ganze funktioniert auch mit Berichten. Gerade diese haben durchaus noch weitere Bereiche wie acGroupLevel1Footer, acGroupLevel2Footer usw...

Freitag, 21. Oktober 2016

WORD Add-In: Funktionalitäten automatisch ausführen sobald ein beliebiges Dokument geöffnet wird


Kürzlich kam die Anforderung auf, das jedes Dokument was geöffnet wird, standardmäßig immer auf 160% automatisch gezoomt werden soll.

Nun ich habe leider keine Einstelloption in Word gefunden, daher habe ich mir kurzerhand ein Add-in geschrieben, welches genau diese (oder auch andere) Aufgabe übernimmt.

Mein erstelltes Add-in kann hier heruntergeladen werden.

Die Datei DefaultFunctions.dotm muss nun einfach in den AutoStart-Ordner von Word gespeichert werden. Dieser müsste standardmäßig der folgende sein:
C:\Users\username\AppData\Roaming\Microsoft\Word\STARTUP

Alternativ kann dieser in Word in den Optionen nachgeschaut werden:
Datei/Optionen/Erweitert/Dateispeicherorte…/AutoStart/Ändern

Jetzt noch Word einmal schließen und neu Starten und schon sollte die Funktionalität aktiv sein.

Die Technik dahinter:
Die Datei DefaultFunctions.dotm wird automatisch geöffnet, sobald Word gestartet wurde, da sie sich ja im AutoStart-Ordner befindet.

In dieser Datei befindet sich eine AutoExec-Prozedur, die (wie der Name es schon ausdrückt) automatisch ausgeführt wird, sobald die Datei geöffnet wurde.

In dieser Prozedur wird eine Klasse instanziiert, welche die Aufgabe hat, die Ereignisse von Word abzufangen und dann dort was zu machen. Uns interessiert hier das Ereignis DocumentOpen, welches ausgelöst wird, sobald eine Datei in Word geöffnet wurde.

Hier der Code dazu:
Option Explicit
    Const ZoomPercentage As Long = 160
    Private cWatcher As clsWatchWD 'Hier wird die Klasse im Speicher gehalten

Public Sub AutoExec()
'Diese Prozedur wird automatisch nach dem Öffnen ausgeführt
    Set cWatcher = New clsWatchWD 'Die Klasse wird instanziiert, somit werden die Ereignisse der aktuellen Word-Instanz in dieser abgefangen (siehe Klasse)
    cWatcher.SetZoomPercentage = ZoomPercentage 'des Wertes für den Zoomfaktor
End Sub

Hier der Code von unserer Klasse clsWatchWD, die den eigentlich Job macht:
Option Explicit
    Private WithEvents cWD As Word.Application 'Durch WithEvents erhalten wir eine ereignisfähige Objektvariable

    Dim cZoomPercentage As Long

Public Property Let SetZoomPercentage(T As Long)
    cZoomPercentage = T 'Übergabe des Wertes für den Zoomfaktor
End Property

Private Sub Class_Initialize()
'Beim instanziieren der Klasse, wird diese Prozedur automatisch ausgeführt
    Set cWD = Application 'Die aktuelle Word-instanz wird in der Ereignisfähigen Objektvariable cWD gespeichert
End Sub

Private Sub cWD_DocumentOpen(ByVal Doc As Document)
'Diese Ereignis-Prozedur wird automatisch ausgeführt, nach dem ein Dokument geöffnet wurde
'Die Variable Doc verweis auf das gerade geöffnete Dokument
'Diese Prozedur kann mit weiteren gewünschten Befehlen erweitert werden...
    With Doc
        'Dieser Abschnitt stellt die Ansicht auf Seitenlayout um, falls dies nicht der Fall sein sollte
        If .ActiveWindow.View.SplitSpecial = wdPaneNone Then
            .ActiveWindow.ActivePane.View.Type = wdPrintView
        Else
            .ActiveWindow.View.Type = wdPrintView
        End If
        'Hier wird das aktuelle Fenster auf den gewünschten Wert gezoomt
        .ActiveWindow.ActivePane.View.Zoom.Percentage = cZoomPercentage
    End With
End Sub

Der Code kann in der Datei eingesehen und angepasst werden, dazu die Datei nicht direkt öffnen, sondern aus dem Windows Explorer mit rechter maustaste und Befehle Öffnen laden.
Mit der bekannten Tastenkombi ALT+F11 wird in den VBA-Editor gewechselt und der Code kann eingesehen und geändert werden.

In der Klasse können noch weitere Ereignis-Prozeduren angelegt werden, je nach Wunsch. z.B. Newdocument, wenn nach dem Erstellen eines neuen Dokumentes was passieren soll.
Oder DocumentBeforeClose, wenn beim Schließen eines Dokumentes was passieren soll, usw...

Dienstag, 14. Juli 2015

Mit Klasse die Access-Gültigkeitsmeldung bei erforderlichen Felder verbessern



Aufgabe 1: Umgang mit erforderlichen Feldern
Enthält eine Tabelle erforderlichen Felder, werden diese erst beim speichern des Datensatzes mit folgender Access-System-Meldung angefordert:








Diese Standard-Meldung ist bescheiden und lässt sich nicht beeinflussen.
Des Weiteren kommt jeweils eine Meldung für jedes einzelne erforderliche Feld, was nicht sehr Anwenderfreundlich ist.

Abhilfe Schaft hier ein VBA-Code, der über das Form-Event BeforeUpdate diesen Fall abfängt, um eine selbstdefinierte Meldung erscheinen zu lassen.
Selbstverständlich möchten wir nun diesen Anwenderfreundliche "Service" in sämtlichen Formularen zur Verfügung stellen.
Es liegt nah den VBA-Code unter BeforeUpdate in die anderen Formularen zu kopieren, mit Anpassung der erforderlichen Felder. Programm-Code der letztendlich dasselbe macht x-mal zu kopieren zeugte schon immer von schlechter Codierung. Hinzu kommt das in gewissen Formularen, die Event Prozedur BeforeUpdate bereits VBA-Code für andere Anforderungen enthält. Also können wir in diesen Fällen den Code nicht einfach 1:1 übertragen.
Daher ist die Idee entstanden den VBA-Code inkl. dem Form-Event BeforeUpdate in einer Klasse auszulagern. Somit muss in den Formularen lediglich die Klasse instanziiert werden, mit Übergabe der gewünschten erforderlichen Felder. Der VBA-Code selbst, der die Funktionalität umsetzt, steht somit nur einmalig in einem Klassenmodul.

Aufgabe 2: Formulare sollen mit den Schaltflächen OK/Abbrechen/Übernehmen versehen werden
Die Schaltfläche 'OK' soll den aktuellen Datensatz speichern und das Formular schliessen.

Die Schaltfläche 'Abbrechen' soll den aktuellen Datensatz verwerfen (mit Rückfrage, falls erforderlich) und das Formular schließen.

Die Schaltfläche 'Übernehmen' soll den aktuellen Datensatz speichern und dabei nur aktiv sein, wenn der Datensatz sich im Änderungsmodus befindet.

Auch hier sollte der VBA-Code und die diversen Event-Prozeduren die dazu nötig sind nicht in den Formularen vervielfältigt werden.
Diese passen Thematisch sehr gut in unsere Klasse mit der Gültigkeitsprüfung, also lagern wir den VBA-Code mit den Form-/Button-Events auch in dieser Klasse aus.

In unseren Formularen benötigen wir nur noch den folgenden kleinen Code-Schnipsel:
Option Compare Database
Option Explicit

    Dim cRV As clsFormRecordValidation

Private Sub Form_Load()
    Set cRV = New clsFormRecordValidation
   
    cRV.AddRequiredField Me.TitleOfCourtesy
    cRV.AddRequiredField Me.LastName
    cRV.AddRequiredField Me.Country
    cRV.AddRequiredField Me.ReportsTo
   
    cRV.ButtonOK = Me.cmdOK
    cRV.ButtonApply = Me.cmdApply
    cRV.ButtonCancel = Me.cmdCancel
End Sub
   
Private Sub Form_Close()
    Set cRV = Nothing
End Sub

Die fertige Klasse integriert in einer Kleinem Beispiel kann hier heruntergeladen werden. Der VBA-Code ist kommentiert, in der Hoffnung, dass das Konzept verständlich wird. Ansonsten einfach Fragen stellen über die Kommentar-Funktion. Kritik (Positiv/Negativ) wie auch Anregungen sind erwünscht.

Ich wünsche weiterhin viel Erfolg mit nicht nur guten VBA-Code, sondern zukünftig auch mit Klasse.