Results 1 to 11 of 11

Thread: Set focus to next TabIndex

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Set focus to next TabIndex

    Hello, in an UserControl that is a control container, I want to send the focus next control outside the UserControl (or previous one).

    I found that in .Net there is a function SelectNextControl that I think does that (just for reference).

    How can I do that in VB6?
    I can get what controls have their windows with WS_TABSTOP style, but I don't know how to get their TabIndex with API.

    I made some code that navigates the child windows and it seemed to work, but then I realized that it gets the next (or previous) window in the Z-Order and not in the TabIndex.

    Here is that code (just for reference):
    Code:
    Private Sub SetFocusToNextControl(nForward As Boolean)
        Dim iHwnd As Long
        Dim iFb As Boolean
        Dim iGW_N As Long
        Dim iGW_F As Long
        
        If nForward Then
            iGW_N = GW_HWNDPREV
            iGW_F = GW_HWNDLAST
        Else
            iGW_N = GW_HWNDNEXT
            iGW_F = GW_HWNDFIRST
        End If
        
        iHwnd = mUserControlHwnd
        iHwnd = GetWindow(iHwnd, iGW_N)
        
        If iHwnd = 0 Then
            iHwnd = GetWindow(mUserControlHwnd, iGW_F)
            iFb = True
        End If
        
        If iHwnd <> 0 Then
            Do Until ((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)
                iHwnd = GetWindow(iHwnd, iGW_N)
                If iHwnd = 0 Then
                    If iFb Then Exit Do
                    iHwnd = GetWindow(mUserControlHwnd, iGW_F)
                    iFb = True
                End If
            Loop
        End If
        If iHwnd <> 0 Then
            SetFocusAPI iHwnd
        End If
    End Sub
    I'm traying to do it with API, but if the TabIndex is not a property of the windows and just something that VB6 stores internally, I'll have to do it with the Parent.Controls collection.
    But before going that route, I wanted to ask if someone knows how to do it.
    Thanks.

  2. #2
    Addicted Member
    Join Date
    Sep 2015
    Posts
    225

    Re: Set focus to next TabIndex

    Code:
    SendKeys "{TAB}"
    Maybe?!!

    You can apply it as many times as the number of controls you have in you container minus the position of the current one has the focus!
    Last edited by labmany; Nov 18th, 2017 at 01:51 AM.

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: Set focus to next TabIndex

    Quote Originally Posted by labmany View Post
    Code:
    SendKeys "{TAB}"
    Maybe?!!

    You can apply it as many times as the number of controls you have in you container minus the position of the current one has the focus!
    Thanks for your answer.

    It is not so easy. The contained controls can have any TabIndex indeed (out of order in regard to the controls ouside the UserControl) and also can have the TabStop set to False, or be windowless.

    Besides, I wouldn't like to use a keyboard simulation approach, but thank you anyway.

    I finally made it using the Parent.Controls collection, but will keep this thread "open" to see if someone have some knowledge about the issue (getting next/previous TabIndex hWnd).

    Here is my current code:
    Code:
    Private Sub SetFocusToNextControl(nForward As Boolean)
        Dim iHwnd As Long
        Dim iControls As Object
        Dim iTi As Long
        Dim iCtl As Control
        Dim iCtlFound As Control
        Dim iTs As Boolean
        Dim iA0 As Boolean
        Dim iTi2 As Long
        
        On Error Resume Next
        Set iControls = UserControl.Parent.Controls
        On Error GoTo 0
        If iControls Is Nothing Then Exit Sub
        
        iTi = -1
        On Error Resume Next
        iTi = UserControl.Extender.TabIndex
        On Error GoTo 0
        If iTi = -1 Then Exit Sub
        
        On Error Resume Next
        If nForward Then
            iHwnd = 0
            iTi = iTi + 1
            Do Until iHwnd <> 0
                Set iCtlFound = Nothing
                For Each iCtl In iControls
                    iTi2 = -1
                    iTi2 = iCtl.TabIndex
                    If iTi2 = iTi Then
                        Set iCtlFound = iCtl
                        Exit For
                    End If
                Next
                If iCtlFound Is Nothing Then
                    iTi = 0
                    If iA0 Then Exit Sub
                    iA0 = True
                Else
                    iHwnd = 0
                    iHwnd = iCtlFound.hWnd
                    If iHwnd <> 0 Then
                        iTs = False
                        iTs = iCtlFound.TabStop
                        If iTs Then
                            If Not (((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)) Then
                                iHwnd = 0
                            End If
                        Else
                            iHwnd = 0
                        End If
                    End If
                    iTi = iTi + 1
                End If
            Loop
        Else ' move focus to the previous control
            iHwnd = 0
            iTi = iTi - 1
            Do Until iHwnd <> 0
                Set iCtlFound = Nothing
                For Each iCtl In iControls
                    iTi2 = -1
                    iTi2 = iCtl.TabIndex
                    If iTi2 = iTi Then
                        Set iCtlFound = iCtl
                        Exit For
                    End If
                Next
                If iCtlFound Is Nothing Then
                    iTi = MaxTabIndex(iControls)
                    If iA0 Then Exit Sub
                    iA0 = True
                Else
                    iHwnd = 0
                    iHwnd = iCtlFound.hWnd
                    If iHwnd <> 0 Then
                        iTs = False
                        iTs = iCtlFound.TabStop
                        If iTs Then
                            If Not (((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)) Then
                                iHwnd = 0
                            End If
                        Else
                            iHwnd = 0
                        End If
                    End If
                    iTi = iTi - 1
                End If
            Loop
        End If
        
        If iHwnd <> 0 Then
            SetFocusAPI iHwnd
        End If
    End Sub
    
    Private Function MaxTabIndex(nControls As Object) As Long
        Dim iCtl As Control
        Dim iTs As Boolean
        Dim iTi As Long
        
        MaxTabIndex = 0
        On Error Resume Next
        For Each iCtl In nControls
            iTs = False
            iTs = iCtl.TabStop
            If iTs Then
                iTi = -1
                iTi = iCtl.TabIndex
                If iTi > MaxTabIndex Then
                    MaxTabIndex = iTi
                End If
            End If
        Next
    End Function

  4. #4
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Set focus to next TabIndex

    There is at least one flaw with your logic. You are not checking whether or not the control you are trying to set focus to is enabled and visible.

    Another simple option would be to toggle the enabled property of the active control. When you disable the active control VB moves to the next control in the tab order, forward navigation only...
    Code:
        Dim oCtrl As Control
        Set oCtrl = ActiveControl
        oCtrl.Enabled = False
        oCtrl.Enabled = True
    Edited: The above would not work for a picturebox that has TabStop=True and also contains controls. Toggling its Enabled property would skip over any controls it contained. The solution is simple. Don't set the TabStop to True for containers. Typically a container that contains other controls, having its TabStop property set to True is pointless. In these cases, the container events are not monitored, are they?

    And yet another option would be to create a collection of objects in sorted tab order. Find the current control in the collection (key = ObjPtr(added control)) and the next/previous item in the collection with TabStop/Enabled/Visible=True would be the one you want to set focus to. This does require a one-time setup at form load, unless you are dynamically adding controls or changing TabStop/TabIndex.
    Last edited by LaVolpe; Nov 18th, 2017 at 12:52 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: Set focus to next TabIndex

    Quote Originally Posted by LaVolpe View Post
    There is at least one flaw with your logic. You are not checking whether or not the control you are trying to set focus to is enabled and visible.
    It does at the line:
    Code:
    If Not (((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)) Then
    Quote Originally Posted by LaVolpe View Post
    Another simple option would be to toggle the enabled property of the active control. When you disable the active control VB moves to the next control in the tab order, forward navigation only...
    I need forward and backward.

    Quote Originally Posted by LaVolpe View Post
    Code:
        Dim oCtrl As Control
        Set oCtrl = ActiveControl
        oCtrl.Enabled = False
        oCtrl.Enabled = True
    Edited: The above would not work for a picturebox that has TabStop=True and also contains controls. Toggling its Enabled property would skip over any controls it contained.
    That's what I need anyway (to send the focus to the next control outside the container).

    Quote Originally Posted by LaVolpe View Post
    The solution is simple. Don't set the TabStop to True for containers.
    In this case I need it because the container can have focus (and do something).

    Quote Originally Posted by LaVolpe View Post
    Typically a container that contains other controls, having its TabStop property set to True is pointless.
    Yes, I agree, typically.

    Quote Originally Posted by LaVolpe View Post
    In these cases, the container events are not monitored, are they?

    And yet another option would be to create a collection of objects in sorted tab order. Find the current control in the collection (key = ObjPtr(added control)) and the next/previous item in the collection with TabStop/Enabled/Visible=True would be the one you want to set focus to. This does require a one-time setup at form load, unless you are dynamically adding controls or changing TabStop/TabIndex.
    Yes, but the TabOrder and TabStop can be changed at run time (althought very unusual) so the right way would be to build the list every time. Not too different from what I did. In fact that was my first idea but I would have needed a way to sort the list (bubble sort or something), so i decided to go for the code I wrote.

  6. #6
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Set focus to next TabIndex

    Apologize for not scrolling your code window far enough to see that you were checking for enabled/visible state. My bad.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  7. #7
    PowerPoster ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    3,046

    Re: Set focus to next TabIndex

    Hi Eduardo,

    try this with API
    Code:
    Option Explicit
    Private Declare Function SetFocusAPI Lib "user32" _
      Alias "SetFocus" ( _
      ByVal hwnd As Long) As Long
    
    Private Sub Form_Load()
     SetFocusAPI Combo1.hwnd
    End Sub
    
    Private Sub Text1_LostFocus()
    SetFocusAPI Command1.hwnd
    End Sub
    regards
    Chris
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: Set focus to next TabIndex

    Quote Originally Posted by LaVolpe View Post
    Apologize for not scrolling your code window far enough to see that you were checking for enabled/visible state. My bad.
    No problem.

    Attached is an example of what I'm trying to achieve.

    Move with the keys Up and Down and see how the focus changes.
    Attached Files Attached Files

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: Set focus to next TabIndex

    I added a function to skip the controls that are inside:

    Code:
    Private Sub SetFocusToNextControl(nForward As Boolean)
        Dim iHwnd As Long
        Dim iControls As Object
        Dim iTi As Long
        Dim iCtl As Control
        Dim iCtlFound As Control
        Dim iTs As Boolean
        Dim iA0 As Boolean
        Dim iTi2 As Long
        
        On Error Resume Next
        Set iControls = UserControl.Parent.Controls
        On Error GoTo 0
        If iControls Is Nothing Then Exit Sub
        
        iTi = -1
        On Error Resume Next
        iTi = UserControl.Extender.TabIndex
        On Error GoTo 0
        If iTi = -1 Then Exit Sub
        
        On Error Resume Next
        If nForward Then
            iHwnd = 0
            iTi = iTi + 1
            Do Until iHwnd <> 0
                Set iCtlFound = Nothing
                For Each iCtl In iControls
                    iTi2 = -1
                    iTi2 = iCtl.TabIndex
                    If iTi2 = iTi Then
                        Set iCtlFound = iCtl
                        Exit For
                    End If
                Next
                If iCtlFound Is Nothing Then
                    iTi = 0
                    If iA0 Then Exit Sub
                    iA0 = True
                Else
                    iHwnd = 0
                    iHwnd = iCtlFound.hWnd
                    If iHwnd <> 0 Then
                        iTs = False
                        iTs = iCtlFound.TabStop
                        If iTs Then
                            If Not (((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)) Then
                                iHwnd = 0
                            ElseIf IsControlWithinUserControlContainer(iCtlFound) Then
                                iHwnd = 0
                            End If
                        Else
                            iHwnd = 0
                        End If
                    End If
                    iTi = iTi + 1
                End If
            Loop
        Else ' move focus to the previous control
            iHwnd = 0
            iTi = iTi - 1
            Do Until iHwnd <> 0
                Set iCtlFound = Nothing
                For Each iCtl In iControls
                    iTi2 = -1
                    iTi2 = iCtl.TabIndex
                    If iTi2 = iTi Then
                        Set iCtlFound = iCtl
                        Exit For
                    End If
                Next
                If iCtlFound Is Nothing Then
                    iTi = MaxTabIndex(iControls)
                    If iA0 Then Exit Sub
                    iA0 = True
                Else
                    iHwnd = 0
                    iHwnd = iCtlFound.hWnd
                    If iHwnd <> 0 Then
                        iTs = False
                        iTs = iCtlFound.TabStop
                        If iTs Then
                            If Not (((GetWindowLong(iHwnd, GWL_STYLE) And WS_TABSTOP) <> 0) And (IsWindowVisible(iHwnd) <> 0) And (IsWindowEnabled(iHwnd) <> 0)) Then
                                iHwnd = 0
                            ElseIf IsControlWithinUserControlContainer(iCtlFound) Then
                                iHwnd = 0
                            End If
                        Else
                            iHwnd = 0
                        End If
                    End If
                    iTi = iTi - 1
                End If
            Loop
        End If
        
        If iHwnd <> 0 Then
            SetFocusAPI iHwnd
        End If
    End Sub
    
    Private Function MaxTabIndex(nControls As Object) As Long
        Dim iCtl As Control
        Dim iTs As Boolean
        Dim iTi As Long
        
        MaxTabIndex = 0
        On Error Resume Next
        For Each iCtl In nControls
            iTs = False
            iTs = iCtl.TabStop
            If iTs Then
                iTi = -1
                iTi = iCtl.TabIndex
                If iTi > MaxTabIndex Then
                    MaxTabIndex = iTi
                End If
            End If
        Next
    End Function
    
    Private Function IsControlWithinUserControlContainer(nCtl As Control) As Boolean
        Dim iContainer As Object
        Dim iControls As Object
        Dim iParent As Object
        Dim iContainer2 As Object
        
        On Error Resume Next
        Set iParent = UserControl.Parent
        Set iControls = iParent.Controls
        If iParent Is Nothing Or iControls Is Nothing Then Exit Function
        
        Set iContainer = Nothing
        Set iContainer = nCtl.Container
        Do Until (iContainer Is Nothing) Or (iContainer Is iParent)
            If iContainer Is UserControl.Extender Then
                IsControlWithinUserControlContainer = True
                Exit Function
            End If
            Set iContainer2 = iContainer
            Set iContainer = Nothing
            Set iContainer = iContainer2.Container
        Loop
    End Function
    The function not only checks if the control is contained in the UserControl, but also if the control is inside other control container that is in the UserControl.

  10. #10
    gibra
    Guest

    Re: Set focus to next TabIndex

    So, you problem now has been RESOLVED?

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: Set focus to next TabIndex

    Quote Originally Posted by gibra View Post
    So, you problem now has been RESOLVED?
    For the moment, yes, but I asked the question with the intention to find out the "proper" way of doing it with API's.
    Or... to confirm that it can't be done with API's.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width