none
Klasse-Event im Formular von Ribbon aufrufen

    Frage

  • Hallo

    Ich beschäftige mich noch nicht lange mit den Ribbons, habe aber schon welche im Einsatz.

    Jetzt zu meiner Frage:
    In jedem meiner Formulare hat es ein Unterformular 'ufrmFormHead'. Ich rufe beim Start jedes Hauptformulares die Klasse clsFormHead mit New clsFormHead auf. Dort kann ich auswählen, welche Schaltflächen mir auf dem ufrmFormHead angezeigt werden sollen, die Grösse, Farbe, Titel und noch diverses anderes. In der Klasse reagiere ich auch auf das Klicken auf die im ufrm vorhandenen Schaltflächen. Hier ein kleiner Auszug aus dem Head meiner Klasse in der Hoffnung, dass man sieht, was alles darin geschieht (ist noch einiges mehr, aber ich möchte niemanden unnötig langweilen).

    Public Event CloseForm(ByRef cancel As Boolean, ByRef CloseApplication As Boolean)
    Public Event PrintForm(ByRef cancel As Boolean)
    Public Event SearchForm()
    Public Event ExcelExport()
    Public Event Edit(ByRef cancel As Boolean, ByRef Clicked As Boolean)    ' Damit kann der Event gecanceled werden.
    Public Event RestoreWindow()
    
    Private WithEvents frmFormContainer As Form    ' Mainform
    Private WithEvents frmFormHead As Form         ' Form frmFormHead
    
    ' Damti beim click auf alle der Buttons auch etwas geschieht
    Private WithEvents btnClose As Access.CommandButton         '(Alt + Shift + O)
    Private WithEvents btnPrint As Access.CommandButton         '(Alt + Shift + P)
    Private WithEvents btnSearch As Access.CommandButton        '(Alt + Shift + F)
    Private WithEvents btnExcel As Access.CommandButton         '(Alt + Shift + E)
    Private WithEvents btnEdit As Access.CommandButton          '(Alt + Shift + D)
    Private WithEvents btnRestoreWindow As Access.CommandButton    '(Alt + Shift + R)

    Soweit funktioniert meine Klasse mittlerweile auch ausgezeichnet.

    Jetzt aber möchte ich diese Buttons in ein Ribbon auslagern. Ribbon erstellen und die Buttons einfügen ist kein Problem. Dieses entsteht erst, wenn ich mir überlege, wie ich die vorhanden Klasse eine Formulares anspreche, um die Funktionalität beizubehalten. Falls es überhaupt möglich ist muss ich von einem allgemeinen Modul (wo die Abarbeitung der Ribbons stattfindet) auf eine Klasse, welche von einem Formular aufgerufen wurde zugreifen. Und das auf jedem geöffneten Formular individuell. Da habe ich keinen Plan für. Wer kann mir hier etwas helfen, mich hier etwas zurecht zu finden?


    Danke und Gruss Thomas

    Montag, 26. Mai 2014 14:23

Antworten

  • Hallo Thomas,

    als ich Deine Antwort gelesen habe, ist mir das auch klar geworden. Das Problem ist, dass in meinem bisherigen Code nur eine einzige Instanz der Klasse verwendet wird. Auf deren Ereignisse hören dann alle Formulare.

    Eine mögliche Lösung hast Du schon gefunden. Die Lösung ist aber nicht schön. Du muss in jedem Formular bei jedem Ereignis der Klasse, auf das Du reagierst, diese Codezeile einfügen. Erstens sieht der Code nicht gut aus und zweitens läufst Du Gefahr, das mal irgendwann zu vergessen. ;-)

    Eine besser Lösung wäre, wenn jedes Formular wieder seine eigene Instanz der Klasse hätte und auf deren Ereignisse reagiert. Dazu musst Du den Code leicht umstellen.

    Den Code im jeweiligen Formular änderst Du wie folgt:

    Private WithEvents thisRL As cls_RibbonListener
    
    Private Sub Form_Open(Cancel As Integer)
        Set thisRL = myRL(Me.Name)
    End Sub
    
    Private Sub thisRL_CloseForm()
        MsgBox "Hier geht die Action ab", vbInformation, Me.Name
    End Sub

    Im Form_Open-Ereignis wird beim Aufruf der Factory-Methode (myRL) als Parameter der Name des Formulars übergeben.

    Den Code im mdlRibbons ergänzt Du ebenfalls um den Namen des gerade aktiven Formulars:

    Sub Exit_onAction(control As IRibbonControl)
       With myRL(Screen.ActiveForm.Name)
           .CloseFrm
       End With
    End Sub

    Hier kommt endlich Screen.ActiveForm zum Einsatz ;-)

    Die größte Änderung erfährt das Modul mit der Factory-Methode:

    Private mcol_RL As New VBA.Collection
    
    Public Property Get myRL(ByVal frmName As String) As cls_RibbonListener
        If ContainsItem(mcol_RL, frmName) = False Then
            Dim RL As cls_RibbonListener
            Set RL = New cls_RibbonListener
            mcol_RL.Add RL, frmName
        End If
        Set myRL = mcol_RL.Item(frmName)
    End Property
    
    Private Function ContainsItem(col As Collection, val As Variant) As Boolean
        Dim itm As Variant
        On Error Resume Next
        itm = col.Item(val)
        ContainsItem = Not (Err.Number = 5 Or Err.Number = 9)
        On Error GoTo 0
    End Function

    Die verschiedenen Instanzen der Klasse cls_RibbonListener werden in einer Collection gespeichert. Als Wert für den Key wird jeweils der Name des Formulars verwendet. Beim Abruf wird jeweils geprüft, ob bereits eine Instanz Klasse für dieses Formular in der Collection vorhanden ist. Bei Bedarf wird diese Instanz erzeugt und in der Collection gespeichert.

    Mit diesen Modifikationen kann man mit einer Klasse formularspezifisch auf die Ereignisse des Ribbons reagieren. Dein Ziel, statt des Unterformulars das Ribbon zu verwenden, solltest Du damit realisieren können.

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de

    Sonntag, 15. Juni 2014 18:00

Alle Antworten

  • Hallo Thomas,

    schau Dir mal bitte Screen.ActiveForm näher an. Damit erhältst Du eine Referenz auf das gerade aktive Formular.

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de


    Samstag, 31. Mai 2014 15:08
  • Hallo Thomas

    Ja, das kenne ich.

    Allerdings begreife ich immer noch nicht, wie ich auf die im Formular mit 'Private' deklarierte Klasse zugreifen kann...

    Beispiel aus meiner Klasse:

    Private WithEvents btnEdit As Access.CommandButton

    Den Button würde es ja nicht mehr geben (wäre neu im Ribbon), der Code, der beim Klick auf den Button ausgeführt wird, sollte aber trotzdem ablaufen (befindet sich in der Klasse). Wie kann ich das erreichen?

    Es kann natürlich auch sein, dass ich die volle Funktionalität von Klassen noch nicht erkannt habe und es einen Weg gibt, den ich bisher noch nicht gekannt habe.


    Danke und Gruss Thomas

    Samstag, 31. Mai 2014 20:04
  • Hallo Thomas,

    vielleicht kannst Du eine kleine Bespieldatenbank bereitstellen. Dann fällt die konkrete Hilfestellung leichter.

    Danke!


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de


    Samstag, 7. Juni 2014 13:55
  • Hallo Thomas

    Natürlich hast Du recht.

    Kannst Du die Datenbank unter https://www.wuala.com/PrimeWulf/Global/?key=GGRGYEVARsul herunterladen?

    Ich habe meine ganze Klasse dort reingetan und ein ganz einfaches Formular und Ribbon erstellt. Die Aktionen im Ribbon sollten genau das Gleiche machen wie die Buttons im Formular.

    Bin für jeden Tipp dankbar.


    Danke und Gruss Thomas

    Dienstag, 10. Juni 2014 20:11
  • Hallo Thomas,

    mit ein paar Zeilen Code, verteilt über ein paar Module kommst Du auch beim Ribbon zum Ziel.

    Ich habe als erstes eine Klasse mit den Namen cls_RibbonListener definiert. Dies ist der Code:

    Public Event CloseForm()
    
    Public Sub CloseFrm()
        RaiseEvent CloseForm
    End Sub

    Dann habe ich in einem Modul eine Factory nach dem Singleton-Pattern implementiert. Der Code sieht wie folgt aus:

    Private m_RL As cls_RibbonListener
    
    Public Property Get myRL() As cls_RibbonListener
        If m_RL Is Nothing Then
            Set m_RL = New cls_RibbonListener
        End If
        Set myRL = m_RL
    End Property

    Im mdlRibbons habe ich dann für den Click auf den Schließen-Button folgenden Code eingefügt:

    Sub Exit_onAction(control As IRibbonControl)
        With myRL
            .CloseFrm
        End With
    End Sub

    Und schließlich noch der Code aus dem Formular:

    Private WithEvents thisRL As cls_RibbonListener
    
    Private Sub Form_Open(Cancel As Integer)
        Set thisRL = myRL
    End Sub
    
    Private Sub thisRL_CloseForm()
        MsgBox "Hier geht die Action ab"
    End Sub

    Und so funktioniert das Ganze:
    Beim Öffnen des Formulars wird in einer Variable eine Instanz der Klasse cls_RibbonListener abgerufen. Diese Instanz wird in der Factory nach dem Singleton-Pattern erstellt. Beim Klick auf den Button im Ribbon wird wiederum eine Instanz der cls_RibbonListener-Klasse abgerufen und die Methode CloseFrm aufgerufen. Diese wiederum wirft das Ereignis CloseForm. Darauf wiederum reagiert der Code im Formular. Fertig.

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de


    Samstag, 14. Juni 2014 10:09
  • Hallo Thomas

    Vielen Dank für Deine Antwort. Natürlich funktioniert Dein Code.

    Jetzt habe ich aber mehere Formulare, welche ich mit dem gleichen Ribbon ausstatten möchte. Wenn ich genau diesen Ribbon für mehr als ein Formular nehme, dann führt Dein Code auch in jedem geöffneten Formular das Gleiche aus. Der Code soll aber natürlich nur im aktuellen Formular ausgeführt werden.

    Muss ich da bei jeder Aktion im Formular ein

        If Screen.ActiveForm.Name = Me.Name Then
            MsgBox "Hier geht die Action ab"
        End If
    
    einfügen? Oder gibt es noch eine sinnvollere Alternative?


    Danke und Gruss Thomas

    Sonntag, 15. Juni 2014 12:53
  • Hallo Thomas,

    als ich Deine Antwort gelesen habe, ist mir das auch klar geworden. Das Problem ist, dass in meinem bisherigen Code nur eine einzige Instanz der Klasse verwendet wird. Auf deren Ereignisse hören dann alle Formulare.

    Eine mögliche Lösung hast Du schon gefunden. Die Lösung ist aber nicht schön. Du muss in jedem Formular bei jedem Ereignis der Klasse, auf das Du reagierst, diese Codezeile einfügen. Erstens sieht der Code nicht gut aus und zweitens läufst Du Gefahr, das mal irgendwann zu vergessen. ;-)

    Eine besser Lösung wäre, wenn jedes Formular wieder seine eigene Instanz der Klasse hätte und auf deren Ereignisse reagiert. Dazu musst Du den Code leicht umstellen.

    Den Code im jeweiligen Formular änderst Du wie folgt:

    Private WithEvents thisRL As cls_RibbonListener
    
    Private Sub Form_Open(Cancel As Integer)
        Set thisRL = myRL(Me.Name)
    End Sub
    
    Private Sub thisRL_CloseForm()
        MsgBox "Hier geht die Action ab", vbInformation, Me.Name
    End Sub

    Im Form_Open-Ereignis wird beim Aufruf der Factory-Methode (myRL) als Parameter der Name des Formulars übergeben.

    Den Code im mdlRibbons ergänzt Du ebenfalls um den Namen des gerade aktiven Formulars:

    Sub Exit_onAction(control As IRibbonControl)
       With myRL(Screen.ActiveForm.Name)
           .CloseFrm
       End With
    End Sub

    Hier kommt endlich Screen.ActiveForm zum Einsatz ;-)

    Die größte Änderung erfährt das Modul mit der Factory-Methode:

    Private mcol_RL As New VBA.Collection
    
    Public Property Get myRL(ByVal frmName As String) As cls_RibbonListener
        If ContainsItem(mcol_RL, frmName) = False Then
            Dim RL As cls_RibbonListener
            Set RL = New cls_RibbonListener
            mcol_RL.Add RL, frmName
        End If
        Set myRL = mcol_RL.Item(frmName)
    End Property
    
    Private Function ContainsItem(col As Collection, val As Variant) As Boolean
        Dim itm As Variant
        On Error Resume Next
        itm = col.Item(val)
        ContainsItem = Not (Err.Number = 5 Or Err.Number = 9)
        On Error GoTo 0
    End Function

    Die verschiedenen Instanzen der Klasse cls_RibbonListener werden in einer Collection gespeichert. Als Wert für den Key wird jeweils der Name des Formulars verwendet. Beim Abruf wird jeweils geprüft, ob bereits eine Instanz Klasse für dieses Formular in der Collection vorhanden ist. Bei Bedarf wird diese Instanz erzeugt und in der Collection gespeichert.

    Mit diesen Modifikationen kann man mit einer Klasse formularspezifisch auf die Ereignisse des Ribbons reagieren. Dein Ziel, statt des Unterformulars das Ribbon zu verwenden, solltest Du damit realisieren können.

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de

    Sonntag, 15. Juni 2014 18:00
  • Hallo Thomas

    Super, ganz vielend Dank.

    Nur noch eine kleine Frage: Da ich das Ribbon in fast allen Formularen verwende, aber jeweils nicht alle Buttons zur Verfügung stellen möchte, habe ich mir überlegt, die nicht benötigten Buttons jeweils auszublenden. Mache ich das jeweils im OnActivate`des Formulares oder gibt es eine andere, bessere Variante?


    Danke und Gruss Thomas


    • Bearbeitet Alphawolfi Dienstag, 17. Juni 2014 08:12
    Dienstag, 17. Juni 2014 07:33
  • Hallo Thomas,

    ich denke, du musst beim Öffnen bzw. Aktivieren eines Formulars dafür sorgen, dass das Ribbon aktualisiert wird. Das geht mit der Invalidate-Methode des Ribbon-Objekts. Details findest Du hier:

    http://www.accessribbon.de/?Access_-_Ribbons:Ribbon_dynamisch_zur_Laufzeit_aktualisieren&search=invalidate

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de



    Mittwoch, 18. Juni 2014 17:50
  • Hallo Thomas

    Das habe ich alles schon in meine Klasse clsFormHead eingebaut und funktioniert sehr gut.

    Jetzt ist mir aber aufgefallen, dass, wenn ich in einem Unterformular bin, das Ribbon des Hauptformulares nicht mehr angezeigt wird (habe das Ribbon als Formularribbon aufgerufen). Ich möchte das aber immer angezeigt haben, unabhängig davon in welchem Control des Formulares ich mich befinde, selbst wenn es ein Unterformular ist. Ich bin sicher Du hast mir dafür auch noch eine Lösung...


    Danke und Gruss Thomas


    • Bearbeitet Alphawolfi Donnerstag, 19. Juni 2014 07:04
    Mittwoch, 18. Juni 2014 21:49
  • Hallo Thomas,

    ich denke, jetzt ist wieder der Zeitpunkt gekommen, an dem Du eine kleine Beispieldatenbank bereitstellen solltest. ;-)

    CU


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de


    Samstag, 21. Juni 2014 08:19
  • Hallo Thomas

    https://www.wuala.com/PrimeWulf/Global/?key=GGRGYEVARsul

    Starte dort frm1 -> Ribbon wird angezeigt
    Klicke ins Ufo -> Ribbon verschwindet. Ich möchte diesen aber während der ganzen Zeit, in welchem ich mich in dem Formular berfinde, angezeigt haben.


    Danke und Gruss Thomas

    Samstag, 21. Juni 2014 08:33
  • Hallo Thomas,

    wenn Du dem Unterformular über die Eigenschaft "Name des Menübands" ebenfalls Dein Ribbon zuweist, funktioniert es.

    HTH


    Thomas@Team-Moeller.de
    Blog: Blog.Team-Moeller.de
    Homepage: www.Team-Moeller.de


    Samstag, 21. Juni 2014 14:29
  • Tja, vielleicht sollte ich Trotz Fussball-WM etwas mehr schlafen, dann wäre ich vielleicht selbst darauf gekommen :o)

    Vielen Dank für Deine Hilfe


    Danke und Gruss Thomas

    Sonntag, 22. Juni 2014 10:10