Consulting

Page 1 of 2 1 2 LastLast
Results 1 to 20 of 38

Thread: vba to keep the focus on userform last used textbox

  1. #1

    vba to keep the focus on userform last used textbox

    Hi All,

    I have created a userform. My team would copy data from other applications and paste it in different text boxes on userform.

    Whenever they use ALt+Tab to toggle between different other excel sheets or application and come back to userform the focus is not staying in userform. it is on a cell in the sheet1 of the workbook that contains the userform. Kindly suggest how can I avoid this.

    Below is what I tried but it wont work.

     
    Private Sub Workbook_Activate()
    TSUF.Show vbModeless
    TSUF.textbox13.setfocus
    End Sub
    
    
    Private Sub Workbook_Deactivate()
    TSUF.Hide
    End Sub
    when I toggle between different applications and comeback to useform I would like the cursor to be in userfom in the last textbox which I updated or edited.

    can someone help me with this.

    Thanks a lot for your time.

  2. #2
    VBAX Regular
    Joined
    May 2014
    Location
    Los Santos
    Posts
    18
    Location
    See if using .Activate instead of .SetFocus makes any difference. You'll need some way of storing which text box was looked at last too, probably in a public variable.

  3. #3
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,453
    Location
    There is no activate method for a userform control.

    @kevvukeka, try setting to the focus to some other control in your code, then setting it to the control you really want to have focus.
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  4. #4
    Hi xld,

    I tried your solution and its working fine. But I am not able to set the focus to the last used textbox or list box. I tried the below code but it will not give me the desired result.



    Private Sub Workbook_Activate()
    TSUF.Show vbModeless
    TSUF.CommandButton3.SetFocus
    
       If TSUF.ListBox1.ListIndex > -1 And TSUF.ListBox1.ListIndex < 6 Then
          If TSUF.TextBox13 = "" Then TSUF.TextBox13.SetFocus
           ElseIf TSUF.TextBox6 = "" Then TSUF.TextBox6.SetFocus
           ElseIf TSUF.TextBox7 = "" Then TSUF.TextBox7.SetFocus
          ElseIf TSUF.TextBox8 = "" Then TSUF.TextBox8.SetFocus
           ElseIf TSUF.TextBox9 = "" Then TSUF.TextBox9.SetFocus
           ElseIf TSUF.TextBox11 = "" Then TSUF.TextBox11.SetFocus
          ElseIf TSUF.ListBox1.ListIndex > 5 Then TSUF.TextBox11.SetFocus
          'Else: TSUF.ListBox1.SetFocus
        End If
    end if
    End Sub

  5. #5
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Apologies up front if I am missing huge amounts, but have you tried saving a reference to what the activecontrol is before hiding the form?

    Mark

  6. #6
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,453
    Location
    Can we see the workbook, and some details on how to reproduce the problem?
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  7. #7
    Hi xld,

    Please find attached the workbook in which user form is created. I need help to keep the focus on the last textbox or list box used while using Alt+Tab as we would be pasting data from different applications in to this user form.

    Before submitting the data I have kept few validations to check so that proper data gets submitted into the file. IF you click submit it may not work for you as the destination path is not accessible to you. you can change it.

    I am not sure if it is due to those list of check points before submitting ,that the user form keeps shifting a little to left side every time I click submit. Kindly help.

    Thanks for your time.


    Hi GTO,

    This is my first user form which I prepared. I am not sure how to store the last accessed textbox or listbox into a variable.


    Thanks a lot for you help.
    Attached Files Attached Files

  8. #8
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Quote Originally Posted by kevvukeka View Post
    ...I have created a userform. My team would copy data from other applications and paste it in different text boxes on userform.

    Whenever they use ALt+Tab to toggle between different other excel sheets or application and come back to userform the focus is not staying in userform...
    Greetings,
    I am not exactly sure if this does what you want; I hope so. I noted that you mention switching to other applications, and I started thinking that checking what window has the focus may be a way to go.

    Anyways, the basic bits are...

    In the Userform's Module:

    Option Explicit
        
    '===API CONSTANTS
      'None
        
    '===API
    Private Declare Function GetForegroundWindow Lib "user32" () As Long
        
    Private Declare Function GetActiveWindow Lib "user32" () As Long
        
    Private Declare Function FindWindow Lib "user32" _
                      Alias "FindWindowA" (ByVal lpClassName As String, _
                                           ByVal lpWindowName As String) As Long
        
    Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, _
                                                    ByVal nIDEvent As Long, _
                                                    ByVal uElapse As Long, _
                                                    ByVal lpTimerFunc As Long) As Long
        
    Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, _
                                                     ByVal nIDEvent As Long) As Long
        
    ' Previous variables
    Public wb1 As Workbook, iwb2 As Workbook
    Public i As Long
    '===Added variables
        
    '===Properties
    Private hWndForm As Long
    Private lTimerRet As Long
    Private bLostFocus As Boolean
    Private sActiveCtrlName As String
        
    'Read-Only
    Public Property Get FormHandle() As Long
      FormHandle = hWndForm
    End Property
    Public Property Get Control01Name() As String
      Control01Name = "TextBox1"
    End Property
    Public Property Get Control02Name() As String
      Control02Name = "TextBox2"
    End Property
        
    'Read-Write
    Public Property Let FocusLost(fl As Boolean)
      bLostFocus = fl
    End Property
      Public Property Get FocusLost() As Boolean
        FocusLost = bLostFocus
      End Property
    Public Property Let ActiveControlName(acn As String)
      sActiveCtrlName = acn
    End Property
      Public Property Get ActiveControlName() As String
        ActiveControlName = sActiveCtrlName
      End Property
        
        
    Public Function ForeWndHwnd() As Long
      ForeWndHwnd = GetForegroundWindow()
    End Function
    Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    Dim lKillTimerRet As Long
      
    If CloseMode = 0 Then
        Cancel = True
        MsgBox "Please use the Exit button on the Userform"
    Else
      '// If we are unloading the form, kill the timer. //
      lKillTimerRet = KillTimer(hWndForm, lTimerRet)
    End If
      
    'Debug.Print "Timer killed in QueryClose = " & (lKillTimerRet <> 0) & "; Cancel = " & CBool(Cancel)
      
    End Sub
        
    Private Sub UserForm_Initialize()
      
      With Me
        '// Change caption to something unlikely to exist in another titlebar, get the handle //
        '// to this form's window, change the caption back.                                   //
        .Caption = "123123123123"
        hWndForm = FindWindow("ThunderDFrame", .Caption)
        .Caption = "TimeSheet - " & Application.UserName
        
        '// Cursory check                                                                     //
        If TypeName(.ActiveControl) = "ListBox" Or TypeName(.ActiveControl) = "TextBox" Then
          sActiveCtrlName = .ActiveControl.Name
        End If
        
      End With
      
      '// Set a timer, passing a pointer to Me.                                               //
      lTimerRet = SetTimer(hWndForm, ObjPtr(Me), 50&, AddressOf TimerProc)
      
      Set wb1 = ThisWorkbook
      
      'code to get username and date on initializing the userform
      TextBox1.Value = Application.UserName
      TextBox2.Value = Format(CDate(Date), "DD-MMM-YYYY")
      'code to add values to the listbox (Task List)
      With ListBox1
        For i = 2 To wb1.Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row
          .AddItem wb1.Sheets(1).Range("A" & i).Value
        Next i
      End With
      
    End Sub
    And the callback in a Standard Module:
    Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal oForm As TSUF, ByVal dwTime As Long)
    Dim lRet As Long
    Dim lPrevHwnd As Long
    Dim sJumpFromCtrl As String
      
      '// It would probably be easier just to stick the API functions in this Standard Module.//
      '// I had just started writing it this way.                                             //
      lRet = oForm.ForeWndHwnd
        
      '// Test to see if the fore window is the form, or any other window.                    //
      If Not lRet = oForm.FormHandle Then
        oForm.FocusLost = True
      Else
        '// If the fore window IS the form, then see if it just received focus again, and if  //
        '// so...                                                                             //
        If oForm.FocusLost Then
          oForm.FocusLost = False
          '// ...try and set the focus to the last active control.                            //
          On Error Resume Next
          If Len(oForm.ActiveControlName) > 0 Then
            If oForm.ActiveControlName = oForm.Control01Name Then
              sJumpFromCtrl = oForm.Control02Name
            Else
              sJumpFromCtrl = oForm.Control01Name
            End If
            oForm.Controls(sJumpFromCtrl).SetFocus
            DoEvents
            'lPrevHwnd = SetFocusAPI(oForm.Frame1.[_GethWnd])
            'Debug.Print lPrevHwnd
            oForm.Controls(oForm.ActiveControlName).SetFocus
          End If
          On Error GoTo 0
        '// Else, if the form does have focus and did not just receive it, track what control //
        '// is active.                                                                        //
        Else
          If TypeName(oForm.ActiveControl) = "ListBox" Or TypeName(oForm.ActiveControl) = "TextBox" Then
            oForm.ActiveControlName = oForm.ActiveControl.Name
          ElseIf TypeName(oForm.ActiveControl) = "Frame" Then
            oForm.ActiveControlName = oForm.Frame1.ActiveControl.Name
          End If
        End If
      End If
    End Sub
    I attached your workbook with these mods. Does that help?

    Mark

  9. #9
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Err... Droool...

    I sorta forgot the attachment. Here it is.
    Attached Files Attached Files

  10. #10
    Hi GTO,

    Thanks a lot for that. I never knew that a concept called pointer exists in this VBA. Its my first year into VBA(You could have know by looking at my code that I wrote).

    I tried to check your file but I get Type mismatch error at this line.

    lTimerRet = SetTimer(hWndForm, ObjPtr(Me), 50&, AddressOf TimerProc)
    I checked in google to find out the use of ObjPtr but couldn't understood a thing.

    So kindly suggest how I should proceed further.

    Thanks for your time.

  11. #11
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Quote Originally Posted by kevvukeka View Post
    Hi GTO,

    Thanks a lot for that. I never knew that a concept called pointer exists in this VBA. Its my first year into VBA(You could have know by looking at my code that I wrote).

    I tried to check your file but I get Type mismatch error at this line.

    lTimerRet = SetTimer(hWndForm, ObjPtr(Me), 50&, AddressOf TimerProc)
    I checked in google to find out the use of ObjPtr but couldn't understood a thing.

    So kindly suggest how I should proceed further.

    Thanks for your time.
    I cannot "see" your end of course, but I am baffled if the error is a miss match, assuming you have lTimerRet dimensioned as a Long. When you say, "I tried to check your file...", do you mean literally you downloaded the attachment and it doesn't run successfully? I downloaded it to my desktop and it ran fine.

    Mark

  12. #12
    yes I downloaded the file...At the beginning I got the popup to declare modify the code to 62 bit attachment. I changed the declarations of variable using Ptrsafe as below:

    Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
        
    Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As Long
        
    Private Declare PtrSafe Function FindWindow Lib "user32" _
                      Alias "FindWindowA" (ByVal lpClassName As String, _
                                           ByVal lpWindowName As String) As Long
        
    Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As Long, _
                                                    ByVal nIDEvent As Long, _
                                                    ByVal uElapse As Long, _
                                                    ByVal lpTimerFunc As Long) As Long
        
    Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As Long, _
                                                     ByVal nIDEvent As Long) As Long
    Is it the change in above declaration that's causing this error? I am not sure.. Kindly suggest.

  13. #13
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Quote Originally Posted by kevvukeka View Post
    yes I downloaded the file...At the beginning I got the popup to declare modify the code to 62 bit attachment. I changed the declarations of variable using Ptrsafe as below:

    Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
    Is it the change in above declaration that's causing this error? I am not sure.. Kindly suggest.
    Oh goodness(!) - I am out of my element here! Off to bed, and I do not have 64-bit, but let me see if I can fix my suggestion a bit (<---no pun) over the next day or two. For the declarations, I believe you'll want to get a copy of Win32API_PtrSafe.txt

    By example, you show GetForegroundWindow as returning a Long, when I believe it should return a LongPtr in 64.

    Declare PtrSafe Function GetForegroundWindow Lib "user32" Alias "GetForegroundWindow" () As LongPtr
    Mark

  14. #14
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,453
    Location
    You should set the hWnd argument from type Long to LongPtr, and the same for lpTimerFunc, and GetForegroundWindow, GetActiveWindow, FindWindow and SetTimer should all return LongPtr.
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  15. #15
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Quote Originally Posted by xld View Post
    You should set the hWnd argument from type Long to LongPtr, and the same for lpTimerFunc, and GetForegroundWindow, GetActiveWindow, FindWindow and SetTimer should all return LongPtr.
    Hi Bob

    I included nIDEvent as per an example I spotted and my (hopefully accurate) take of:

    nIDEvent [in]Type: UINT_PTR

    wherein I think that means an unsigned integer pointer?

    I realize your response may have been aimed at the OP, but I might as well practice. Hopefully this is correct:

    Userform declaration section and affected module-level variables:

    '===API
    #If VBA7 Then
      
    Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, _
                                                            ByVal nIDEvent As LongPtr, _
                                                            ByVal uElapse As Long, _
                                                            ByVal lpTimerFunc As LongPtr _
                                                            ) As LongPtr
      
    Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, _
                                                             ByVal nIDEvent As LongPtr _
                                                             ) As Boolean
      
    Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr
      
    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, _
                                                                                  ByVal lpWindowName As String _
                                                                                  ) As LongPtr
      
    #Else
      
    Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, _
                                                    ByVal nIDEvent As Long, _
                                                    ByVal uElapse As Long, _
                                                    ByVal lpTimerFunc As Long _
                                                    ) As Long
      
    Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, _
                                                     ByVal nIDEvent As Long _
                                                     ) As Long
      
    Private Declare Function GetForegroundWindow Lib "user32" () As Long
        
    Private Declare Function FindWindow Lib "user32" _
                      Alias "FindWindowA" (ByVal lpClassName As String, _
                                           ByVal lpWindowName As String) As Long
      
    #End If
        
    #If VBA7 Then
    Private hWndForm As LongPtr
    #Else
    Private hWndForm As Long
    #End If
    Affected properties/function in userform:
    'Read-Only
    #If VBA7 Then
    Public Property Get FormHandle() As LongPtr
    #Else
    Public Property Get FormHandle() As Long
    #End If
      FormHandle = hWndForm
    End Property
    
    #If VBA7 Then
    Public Function ForeWndHwnd() As LongPtr
    #Else
    Public Function ForeWndHwnd() As Long
    #End If
      ForeWndHwnd = GetForegroundWindow()
    End Function
    Modified callback:
    Option Explicit
        
    #If VBA7 Then
      
    Public Sub TimerProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal oForm As TSUF, ByVal dwTime As Long)
    Dim lRet As LongPtr
      
    #Else
      
    Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal oForm As TSUF, ByVal dwTime As Long)
    Dim lRet As Long
      
    #End If
      
    Dim sJumpFromCtrl As String
      
      lRet = oForm.ForeWndHwnd
      
      If Not lRet = oForm.FormHandle Then
        oForm.FocusLost = True
      Else
        
        If oForm.FocusLost Then
          
          oForm.FocusLost = False
          
          On Error Resume Next
          If Len(oForm.ActiveControlName) > 0 Then
            If oForm.ActiveControlName = oForm.Control01Name Then
              sJumpFromCtrl = oForm.Control02Name
            Else
              sJumpFromCtrl = oForm.Control01Name
            End If
            oForm.Controls(sJumpFromCtrl).SetFocus
            DoEvents
            oForm.Controls(oForm.ActiveControlName).SetFocus
          End If
          On Error GoTo 0
        
        Else
          
          If TypeName(oForm.ActiveControl) = "ListBox" Or TypeName(oForm.ActiveControl) = "TextBox" Then
            oForm.ActiveControlName = oForm.ActiveControl.Name
          ElseIf TypeName(oForm.ActiveControl) = "Frame" Then
            oForm.ActiveControlName = oForm.Frame1.ActiveControl.Name
          End If
        
        End If
      
      End If
    End Sub
    Hi Kevvukeka,

    I believe I have this correct, but I would suggest waiting until XLD or another member used to the new declarations comments. Or, at least I would have only the one workbook open and nothing unsaved before I ran it. As I think I mentioned, I am unaware of any of the PCs where I work having Office installed in 64-bit, so the most I can assuredly say is that it runs in 32-bit Excel 2010.

    Mark

  16. #16
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    For some reason the #End If's got cut-off in the rendering, but they are there. Here's the modified wb.
    Attached Files Attached Files

  17. #17
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Sorry All,

    I thought to ask this after the Edit expired...

    @xld:

    Just if you have time, does my solution seem on-track, or would you do something different?

    Thank you so much,

    Mark

  18. #18
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,453
    Location
    Haven't looked at it closely Mark, but it looks about right and I would definitely use conditional compilation.
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  19. #19
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    @xld:

    Thank you on both counts. Hopefully the OP will be able to verify it runs in 64. My last question was more at would you use a timer (or similar callback) for when other apps are active?

    Again, thank you very much. As a 'not-a-real/professional coder' - coder, some of this stuff is a bit mystifying (C++ and API in general) and I often miss something Excel may have built-in. I hope that makes sense?

    BTW - how's your weather been? I am thinking this must be a nice time of year there. Just struck me, as we seem to have jumped into Monsoon season. Nothing devastating mind you; just awfully dusty, humid and hot, all at once...

    Mark

  20. #20
    VBAX Expert
    Joined
    Oct 2012
    Posts
    726
    Location
    Maybe this does a similar thing without the api calls?

    'in the form
    Private Sub UserForm_Activate()
        Set frm = UserForm1
        delay = (1 / 24 / 60 / 60) * 3 '3 seconds
        tmrfocus
    End Sub
    
    'in a module
    Public frm As UserForm, delay As Single
    Sub tmrfocus()
        On Error Resume Next
        Dim p As Integer, l As Integer
        With frm.ActiveControl
            If TypeName(frm.ActiveControl) = "TextBox" Then
                p = .SelStart
                l = .SelLength
                For Each c In frm.Controls
                    If c.Name <> .Name Then
                        c.SetFocus
                        Exit For
                    End If
                Next
                .SetFocus
                .SelStart = p
                .SelLength = l
                Application.OnTime Now + delay, "tmrfocus"
            End If
        End With
    End Sub
    Anyway, this only seems to be a problem if the form isn't modal so the simple question is, does it really need to be modeless?

Posting Permissions

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