I have to set ActiveX control tab order in MS Word using VBA. So here is the basic code:
Private Sub radioFull_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
If KeyCode = 9 Then
radioIntern.Activate
End If
End Sub
Problem is I have an active Restrict Editing Protection on the document set by password. Thus after starting protection, while pressing a tab on any control, it deny to functioning saying that I have a protection on the document.
So, during execution of the above function, I first have to un-protect the document, moving tab to next field and then re-protect by the following function:
Private Sub ToggleProtect()
If ActiveDocument.ProtectionType <> wdNoProtection Then
ActiveDocument.Unprotect Password:="password"
Else
ActiveDocument.Protect Password:="password", NoReset:=True, _
Type:=wdAllowOnlyFormFields, _
UseIRM:=False, EnforceStyleLock:=False
End If
End Sub
Private Sub radioFull_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
If KeyCode = 9 Then
ToggleProtect
radioIntern.Activate
ToggleProtect
End If
End Sub
It works well. So I intend to shorten the main code a little bit more by something like this:
Private Sub radioFull_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
tabOrder(KeyCode, controlName)
End Sub
and the tabOrder function in this case like the follwoing:
Public Sub tabOrder(K as integer,t as string)
If KeyCode = K Then
ToggleProtect
t.Activate
ToggleProtect
End If
End Sub
But I am not familiar on VBA function argument. So please tell me how to pass the argument or write the function correctly so that I can maintain tab order in MS Word form?
Even though the MS Forms controls are derived from MSForms.Control VBA is apparently unable to "cast" them to this data type. It can work with the general type, however. The trick is to declare the procedure argument as data type Variant.
While I was at it, I made another small optimization to the code by declaring an object variable of type Word.Document for passing the document to ToggleProtect. While it's unlikely, it is theoretically possible that the user will change documents during code execution, making the ActiveDocument a different one than that which triggered the code. So if you get the target document immediately then the code will always execute on the correct document, no matter which one currently has the focus.
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
Dim doc As Word.Document
Set doc = Me
tabOrder KeyCode, doc, Me.TextBox1
End Sub
Public Sub tabOrder(ByVal KeyCode As MSForms.ReturnInteger, _
ByRef doc As Word.Document, ByRef t As Variant)
If KeyCode = 9 Then
ToggleProtect doc
t.Activate
ToggleProtect doc
End If
End Sub
Private Sub ToggleProtect(doc As Word.Document)
If doc.ProtectionType <> wdNoProtection Then
doc.Unprotect Password:="password"
Else
doc.Protect Password:="password", NoReset:=True, _
Type:=wdAllowOnlyFormFields, _
UseIRM:=False, EnforceStyleLock:=False
End If
End Sub
In your KeyDown event, it looks like you want to pass the KeyCode and the Control. Therefore, the arguments you pass must match the signature of the tabOrder sub. Look how KeyCode is defined and copy/paste to your tabOrder sub. The second argument will be defined as Control allowing for any control to be passed. Here is an example of what I am talking about:
Private Sub radioFull_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
tabOrder KeyCode, radioFull
End Sub
Public Sub tabOrder(ByVal KeyCode As MSForms.ReturnInteger, ByRef t As MSForms.Control)
If KeyCode = 9 Then
ToggleProtect
t.Activate
ToggleProtect
End If
End Sub
Related
I have a userform with programmatically created comboboxes, which I need to run events on. As per the advise here, I created a wrapper class which I put around each such combobox ("event listener").
This is the rough content of the clsEvntListnr class module
Public WithEvents cb As MSForms.ComboBox
Public frm As UserForm
Private Sub cb_Change()
CollectGarbage
End Sub
Private Sub cb_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
'stuff
End Sub
Private Sub cb_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
'stuff
End Sub
Private Sub cb_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
'stuff
End Sub
Private Sub cb_DropButtonClick()
'stuff
End Sub
Private Sub cb_Enter()
'stuff
End Sub
Private Sub cb_Exit()
'stuff
End Sub
Private Sub cb_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'stuff
End Sub
Private Sub cb_Click()
'stuff
End Sub
Private Sub cb_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'stuff
End Sub
Private Sub cb_AfterUpdate()
'stuff
End Sub
This is how the comboboxes are created (as a part of an event for another combobox). The C_COMBOS at the end is a globally declared collection.
Private Sub cbTransaction_Change()
Dim oEvtListnr As clsEventListener
'other stuff and declarations
For i = LBound(var) To UBound(var)
Set ctrl = Me.Controls.Add("forms.combobox.1", "ctrlTb" & i, True)
Set oEvtListnr = New clsEventListener
Set oEvtListnr.cb = ctrl
Set oEvtListnr.frm = Me
C_COMBOS.Add oEvtListnr
next i
End sub
Now the behaviour is mostly as expected with the exceptions that certain event just will not fire. From the events I defined in the class module, the following do fire:
cb_KeyDown, cb_KeyPress, cb_KeyUp, cb_DropButtonClick, cb_DblClick, cb_MouseUp
while these do not:
cb_Change, cb_Click, cb_Enter, cb_Exit, cb_AfterUpdate
I have made the obvious tests by putting in breaks into these events and indeed they simply do not fire up. Any idea what may be the issue?
I have the following form in excel. It is part of a simple inventory worksheet, and is used to update the status of items. It was functioning as intended, however when I tried to add in a checkbox to make the selected status persist (allowing me to only type in the serial rather than serial and status every time when working with a batch of items) I noticed that the focus after submitting cycles forward as if I pressed tab rather than being where I set it with SetFocus.
I'm presuming this is an oversight related to either the event cycle for KeyDown or nested If/Else logic, but I've had no luck actually diagnosing it. I've only recently started using VBA, and there are a lot of quirks I'm trying to understand.
Private Sub clear()
Me.txtSerial = ""
If cbPersist.Object.Value = False Then
Me.cboxStatus = ""
Me.cboxStatus.SetFocus
Else
Me.txtSerial.SetFocus
End If
End Sub
Private Sub submit()
Dim loc As Range
Dim ws As Worksheet
Set ws = Worksheets("Full Inventory")
Set loc = ws.Columns("B").Find(what:=Me.txtSerial.Value, LookIn:=xlValues, lookat:=xlWhole)
If Not loc Is Nothing Then
ActiveWindow.ScrollRow = loc.Row
ws.Cells(loc.Row, 10).Value = Me.cboxStatus.Value
Else
MsgBox "Serial not found."
End If
clear
End Sub
Private Sub txtSerial_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = vbKeyReturn Then
submit
ElseIf KeyCode = vbKeyEscape Then
clear
End If
End Sub
Private Sub UserForm_Initialize()
cboxStatus.List = Array("Stock", "Shipped", "Research", "Sent Back", "Return")
End Sub
Suggest the following:
Code snippet of UserForm module
Private Sub txtSerial_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = vbKeyReturn Then
submit
KeyCode = vbKeyPageDown ' << modify Enter key value to prevent from tab hopping
ElseIf KeyCode = vbKeyEscape Then
clear
End If
End Sub
How to move cursor in text box with enter in text box?
Here is my code, it gives me a syntax error.
Private Sub TextBox2_Change()
Sheets("30").Range("D18") = TextBox2.Value
TextBox2.Enter Then Sheets("30").Range("E19").Select
End Sub
Instead of using the Change event, use the KeyUp event and check for the KeyCode vbKeyReturn:
Private Sub TextBox2_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = KeyCodeConstants.vbKeyReturn Then
Sheets("30").Range("E19").Select
End If
End Sub
I've built a form in Excel. It consists of 3 command buttons and a frame containing checkboxes. The checkboxes are dynamically populated at userform_initialize based on tables in an excel sheet (the idea being easy user customization). The reason for the frame is that there can be a lot of checkboxes and I want the user to be able to scroll through them.
My goal now is to create keyboard shortcuts for the form. Where I get stuck is that I can't brute force write KeyDown handlers for each of the checkboxes because I don't know which ones will exist. I realize that it would also just be better if I could have the event handler at the form level. Googling has found me the form's KeyPreview property. Unfortunately, the properties window in VBA IDE doesn't show it and when I try to access it programmatically by setting Me.KeyPreview = True at userform_initialize VBA throws a compile error: "Method or data member not found" - what I would expect given it isn't in the properties window, but was worth a try.
I feel like there's something I'm obviously missing so I thought I'd ask before spending time learning how to write and then rewriting the form entirely as a class as in the MSDN example code:
https://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview(v=vs.110).aspx.
Am I that lucky?
I confess to being at the limit of my VBA knowledge and I'm looking to go expand on it. Any general concepts or context I should red would be greatly appreciated.
UPDATE
I'm now thinking about GetAsyncKeyState and Application.Onkey.
From what I understand, GetAsyncKeyState only works within an infinite DoEvents loop. I tried initiating one hoping the form would still load but of course it didn’t – I’m stuck in the loop.
The problem with Application.Onkey is that I can't assign the event function to the key within the userform module. This puzzles me because other event handlers can go in the userform module. In fact, I’d put it in the Userform_Initialize procedure. Is it because it's not a form event but an application event?
EDIT
I seem to have something that works, but for the strange issue described here:
Event handling class will not fire unless I use a breakpoint when initializing form
Thank you #UGP
Here is an example how it could work, found here:
To put in a class named "KeyPreview":
Option Explicit
Dim WithEvents u As MSForms.UserForm
Dim WithEvents t As MSForms.TextBox
Dim WithEvents ob As MSForms.OptionButton
Dim WithEvents lb As MSForms.ListBox
Dim WithEvents dp As MSComCtl2.DTPicker
Event KeyDown(ByVal KeyCode As Integer, ByVal Shift As Integer)
'Event KeyPress(ByVal KeyAscii As Integer)
Private FireOnThisKeyCode As Integer
Friend Sub AddToPreview(Parent As UserForm, KeyCode As Integer)
Dim c As Control
Set u = Parent
FireOnThisKeyCode = KeyCode
For Each c In Parent.Controls
Select Case TypeName(c)
Case "TextBox"
Set t = c
Case "OptionButton"
Set ob = c
Case "ListBox"
Set lb = c
Case "DTPicker"
Set dp = c
End Select
Next c
End Sub
Private Sub u_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = FireOnThisKeyCode Then RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub t_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = FireOnThisKeyCode Then RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub ob_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = FireOnThisKeyCode Then RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub lb_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = FireOnThisKeyCode Then RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub dp_KeyDown(KeyCode As Integer, ByVal Shift As Integer)
If KeyCode = FireOnThisKeyCode Then RaiseEvent KeyDown(KeyCode, Shift)
End Sub
To put in the userform:
Option Explicit
Dim WithEvents kp As KeyPreview
Private Sub UserForm_Initialize()
Set kp = New KeyPreview
kp.AddToPreview Me, 114
End Sub
Private Sub kp_KeyDown(ByVal KeyCode As Integer, ByVal Shift As Integer)
MsgBox "F3 was pressed..."
End Sub
It works with TextBoxes, OptionButtons, ListBoxes and DTPickers. Other Controls that could get focus will need to be handled aswell.
I'm creating a Pop Up Menu to paste into an ActiveX Textbox on an Excel worksheet. The pop up works but the "Paste" option is grayed out.
Private Sub txtInput_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = vbKeyRButton Then
Call ShowMenu
Application.CommandBars("MyMenu").ShowPopup
End If
End Sub
Sub ShowMenu()
'Remove any old instance of MyPopUp
On Error Resume Next
CommandBars("MyMenu").Delete
On Error GoTo 0
With CommandBars.Add(name:="MyMenu", Position:=msoBarPopup)
With .Controls.Add(Type:=msoControlButton, ID:=22)
.Enabled = True
End With
End With
End Sub
I added in the .Enabled = True but that did not fix the issue. I'm sure I'm missing something basic.
Additional question, once the user can click paste, do I HAVE to add OnAction and refer to a sub to have it actually paste the text into the textbox or is using the msoControlButton with ID 22 enough to indicate the pasting of text?
Additional question, once the user can click paste, do I HAVE to add OnAction and refer to a sub to have it actually paste the text into the textbox or is using the msoControlButton with ID 22 enough to indicate the pasting of text?
No, you actually don't need to use either, because the ActiveX TextBox class has a Paste method which you can use. So, piggy-backing on #Mukul Varney's answer, within the cmdPasteButton_Click event procedure, you can simply do:
txtInput.Paste
And this should paste the clipboard contents at the cursor position in the TextBox.
Private Sub cmdPasteButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
txtInput.Paste
CancelDefault = True
End Sub
Try below. Paste is enabled for me.
Private WithEvents cmdPasteButton As CommandBarButton
Private Sub txtInput_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = vbKeyRButton Then
Call ShowMenu
Application.CommandBars("MyMenu").ShowPopup
End If
End Sub
Sub ShowMenu()
'Remove any old instance of MyPopUp
On Error Resume Next
CommandBars("MyMenu").Delete
On Error GoTo 0
Set cmdPasteButton = CommandBars.Add(Name:="MyMenu", Position:=msoBarPopup).Controls.Add(Type:=msoControlButton, ID:=22)
cmdPasteButton.OnAction = "Textbox_Paste"
End Sub
Private Sub cmdPasteButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
MsgBox "hello from cmdPasteButton_Click"
CancelDefault = True
End Sub