Access

Determinar la pestaña de la cinta actualmente seleccionada

Recientemente me encontré con un hilo en UtterAccess donde un usuario preguntaba cómo se podía identificar la pestaña de la cinta actualmente seleccionada en su aplicación.

¿Cómo puedo saber qué pestaña de la cinta está seleccionada actualmente?

Al ver que nadie mordisqueaba decidí dar mi granito de arena.

La solución típica

Mi instinto inicial fue sugerir

almacene la identificación de la pestaña cada vez que se realiza una selección, luego puede consultarla en cualquier momento para saber cuál está activa actualmente.

Si está implementando su propia cinta y código, es posible detectar cuándo se seleccionan las pestañas y, por lo tanto, podría guardar esta información antes de realizar otras acciones.

Una mejor solución

No me gustó particularmente el enfoque anterior y estaba interesado en encontrar una solución universal que pudiera simplemente insertarse en cualquier aplicación de Office.

Luego recordé que había desarrollado algo de UIAutomation hace un par de años, así que lo desenterré y comencé a jugar. Después de un tiempo, desarrollé una solución:

'---------------------------------------------------------------------------------------
' Procedure : UIA_GetSelectedRibbonTab
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Identify the currently selected ribbon tab
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: UIAutomationClient
' Dependencies: oUIA()
'
' Usage:
' ~~~~~~
' Debug.Print UIA_GetSelectedRibbonTab
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2024-12-12              Initial Release - UA Help
'---------------------------------------------------------------------------------------
Public Function UIA_GetSelectedRibbonTab() As String
    Dim oUIAAccess                          As UIAutomationClient.IUIAutomationElement
    Dim oUIAElementRibbon                   As UIAutomationClient.IUIAutomationElement
    Dim oUIAElementTab                      As UIAutomationClient.IUIAutomationElement
    Dim oUIAElementSelectionItemPattern     As UIAutomationClient.IUIAutomationSelectionItemPattern
    Dim aoUIAElementRibbonChildren          As UIAutomationClient.IUIAutomationElementArray
    Dim lCounter                            As Long

    'Set oUIAAccess = UIA_Find_DbElement(Application.hWndAccessApp) ' Access
    Set oUIAAccess = UIA_Find_DbElement(Application.hWnd)  ' Excel
    If Not (oUIAAccess Is Nothing) Then
        Set oUIAElementRibbon = UIA_FindElement_NameAndClass(oUIAAccess, "Ribbon Tabs", "NetUIPanViewer")
        If Not (oUIAElementRibbon Is Nothing) Then
            Set aoUIAElementRibbonChildren = oUIAElementRibbon.FindAll(TreeScope_Children, oUIA.CreateTrueCondition)
            For lCounter = 0 To aoUIAElementRibbonChildren.Length - 1
                If aoUIAElementRibbonChildren.GetElement(lCounter).CurrentClassName = "NetUIRibbonTab" Then
                    Set oUIAElementTab = aoUIAElementRibbonChildren.GetElement(lCounter)
                    If Not (oUIAElementTab Is Nothing) Then
                        Set oUIAElementSelectionItemPattern = oUIAElementTab.GetCurrentPattern(UIA_SelectionItemPatternId)
                        If oUIAElementSelectionItemPattern.CurrentIsSelected = 1 Then
                            UIA_GetSelectedRibbonTab = oUIAElementTab.CurrentName
                            Exit For
                        End If
                    End If
                End If
            Next
        End If
    End If
End Function

Código de ayuda

Private pUIA As UIAutomationClient.CUIAutomation '.IUIAutomation6 .IUIAutomation

Public Function oUIA() As UIAutomationClient.CUIAutomation '.IUIAutomation6 .IUIAutomation
    If pUIA Is Nothing Then
        Set pUIA = New UIAutomationClient.CUIAutomation
    End If
    Set oUIA = pUIA
End Function

'---------------------------------------------------------------------------------------
' Procedure : UIA_Find_DbElement
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Use to return the IUIAutomationElement of the HWND
'               For our purposes the current database
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: UIAutomationClient
' Dependencies: oUIA()
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' Hwnd      : The HWND of the element we want to bind to
'
' Usage:
' ~~~~~~
' Set oUIAAccess = UIA_Find_DbElement(Application.hWndAccessApp)
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-07-06              Dev
' 2         2022-10-22              Initial Release
'---------------------------------------------------------------------------------------
Public Function UIA_Find_DbElement(ByVal hWnd As Long) As UIAutomationClient.IUIAutomationElement
On Error GoTo Error_Handler
'    Dim oUIA                 As UIAutomationClient.CUIAutomation '.IUIAutomation6 .IUIAutomation
'
'    Set oUIA = New UIAutomationClient.CUIAutomation
    Set UIA_Find_DbElement = oUIA.ElementFromHandle(ByVal hWnd)

Error_Handler_Exit:
    On Error Resume Next
'    Set oUIA = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: UIA_Find_DbElement" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

'---------------------------------------------------------------------------------------
' Procedure : UIA_FindElement_NameAndClass
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Retrieve the matching element name with the specified class in the subtree
'               of the passed element
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: UIAutomationClient
' Dependencies: oUIA()
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' Application   : Element to search the subtree of
' sName         : Name of the element being searched for
' sClass        : Name of the Class to match
'
' Usage:
' ~~~~~~
' Set oUIAElement = UIA_FindElement_NameAndClass(UIA_Find_DbElement(Application.hWndAccessApp), _
'                                                "Clear Search String", "NetUINavPaneGroup")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-10-25              Initial Release
'---------------------------------------------------------------------------------------
Public Function UIA_FindElement_NameAndClass(ByRef oUIAStartElement As UIAutomationClient.IUIAutomationElement, _
                                             ByVal sName As String, _
                                             ByVal sClass As String) As UIAutomationClient.IUIAutomationElement
    Dim oUIA                  As UIAutomationClient.CUIAutomation
    Dim oUIACondition         As UIAutomationClient.IUIAutomationCondition
    Dim oUIAElement           As UIAutomationClient.IUIAutomationElement

    Set oUIA = New CUIAutomation
    Set oUIACondition = oUIA.CreateAndCondition(oUIA.CreatePropertyCondition(UIA_NamePropertyId, sName), _
                                                oUIA.CreatePropertyCondition(UIA_ClassNamePropertyId, sClass))
    Set oUIAElement = oUIAStartElement.FindFirst(TreeScope_Subtree, oUIACondition)

    Set UIA_FindElement_NameAndClass = oUIAElement

    Set oUIAElement = Nothing
    Set oUIACondition = Nothing
    Set oUIA = Nothing
End Function

Es importante tener en cuenta que para que este código funcione, primero debe agregar una referencia de VBA a ‘UIAutomationClient’.

¿Cómo funciona exactamente?

Es bastante sencillo, aunque el código pueda parecer desalentador.

Primero, vinculamos la instancia UIAutomation de nuestra aplicación, en nuestro caso Microsoft Access usando su HWND:

Set oUIAAccess = UIA_Find_DbElement(Application.hWndAccessApp)

Luego, encuentro el elemento Ribbon UIAutomation:

Set oUIAElementRibbon = UIA_FindElement_NameAndClass(oUIAAccess, "Ribbon Tabs", "NetUIPanViewer")

Ahora que tenemos la cinta con la que trabajar, buscamos e iteramos sobre sus elementos secundarios:

Set aoUIAElementRibbonChildren = oUIAElementRibbon.FindAll(TreeScope_Children, oUIA.CreateTrueCondition)
For lCounter = 0 To aoUIAElementRibbonChildren.Length - 1
'...
Next

Debido a la forma en que se crea la Cinta a partir de diferentes tipos de elementos, en la iteración, limito específicamente la búsqueda a pestañas, aquellas con una clase de ‘NetUIRibbonTab’:

If aoUIAElementRibbonChildren.GetElement(lCounter).CurrentClassName = "NetUIRibbonTab" Then
'...
End If

Si hemos llegado hasta ahora en el proceso, entonces el elemento con el que estamos tratando es una pestaña de cinta, por lo que configuro oUIAElementTab en el elemento iterado actual (para que podamos seguir trabajando con él):

Set oUIAElementTab = aoUIAElementRibbonChildren.GetElement(lCounter)

Por último, creamos una variable de patrón UIAutomation para que finalmente podamos verificar el valor CurrentIsSelected de la pestaña (0 => no está seleccionado, 1 => seleccionado)

Set oUIAElementSelectionItemPattern = oUIAElementTab.GetCurrentPattern(UIA_SelectionItemPatternId)
    If oUIAElementSelectionItemPattern.CurrentIsSelected = 1 Then
        UIA_GetSelectedRibbonTab = oUIAElementTab.CurrentName
        Exit For
    End If

Este fue un viaje emocionante ya que revisé algunos conceptos que exploré hace un par de años y los adapté para crear algo completamente nuevo, algo que nunca había visto antes y ciertamente nunca lo había intentado. Continuamente me sorprende lo que podemos lograr con VBA, impulsados ​​por nuestra determinación de persistir y experimentar con nuevas ideas.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba