I'm newbie in word-vba (just to let you know that my question could be really stupid).
I would like to clear a textbox only when I click in the textbox the first time.
I've tried For... Next but I wasn't able to comfigure it correctly
Private Sub SWName_Field_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
SWName_Field.Text = ""
End Sub
I would like that the code will work exactly the same way it works but when I put some text and for example the user make a mistake or typo error the second click in the textbox shouldn't clear the text inside.
Thank you for the support
There's no inbuilt activity state identifier in any UserForm control. So you need to use meta data to specify and identify whether or not your mousedown is happening for the first time.
Use Tag property of the control for that.
See the code comments for details.
Private Sub TextBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'/ Use the Tag field to determine and store the state of Text Box.
If Len(Me.TextBox1.Tag) < 1 Then
'/ If Mousedown for the very first time then TextBox's tag is empty.
'/ Go ahead, clean the textbox.
'/ And set a text in tag.
Me.TextBox1.Text = ""
Me.TextBox1.Tag = "Text Cleared"
End If
End Sub
You can use a Static local variable to "remember" whether the handler was executed at least once or not:
Private Sub SWName_Field_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Static executed As Boolean
If Not executed Then
SWName_Field.Text = ""
executed = True
End If
End Sub
The state of the Static local is tied to your UserForm instance - the value will be "remembered" for as long as the form instance is alive.
This means, if you're showing the form's default instance, the state won't necessarily be reset. You will want to ensure you get a fresh default form state every time the form is shown, not just the first time - to do this you New up the form:
With New UserForm1
.Show
End With
If you just do UserForm1.Show, then you don't control when the form instance gets created - VBA does.
You'll also want to control when the form instance gets destroyed - you can do that by handling the form's QueryClose event:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True ' cancel the destruction of the object
Me.Hide ' hide the instance instead
End If
End Sub
With that, the object will be destroyed when execution reaches End With. Without it, the object will be destroyed if the user clicks the "X" button, and you probably don't want that to happen (especially if you need to access the form's state after it's closed).
Related
I want a certain function to be run every time someone clicks on a textbox in my form. So far I have managed to detect when it gets focus and when it is doubleclicked, using the following two eventhandlers, but I am unsure about how to catch when it is clicked once while it already has focus.
Does anyone here have any experience with catching such an event? I don't seem to find any obvious suspects in the dropdown-menus of the VBA editor.
Private Sub tbxTil_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
Me.tbxTil = format(oppdater_dato(CDate(Me.tbxTil)), "dd.mm.yy", vbMonday, vbFirstFourDays)
End Sub
Private Sub tbxTil_Enter()
Me.tbxTil = format(oppdater_dato(CDate(Me.tbxTil)), "dd.mm.yy", vbMonday, vbFirstFourDays)
End Sub
You can use the Mouse events, something like this:
Private Sub tbxTil_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
MsgBox "Click"
End Sub
I am creating a macro program to record and play back mouse and keyboard input. Recording works fine, as does mouse playback, but I am having trouble in playing back the keyboard input - specifically pressing and holding a key for several seconds before releasing. This is not equivalent to repetitively pressing the key. This is what I have tried:
Technique 1: Me.KeyDown
Private Sub keyboard_pressed() Handles Me.KeyDown
Dim keypress = e.KeyData
MsgBox(keypress)
End Sub
Only works when window is in focus.
Technique 2: SendKeys
Private Sub timer_keyboardplayback_Tick() Handles timer_playback.Tick
SendKeys.Send("{LEFT}")
timer_playback.Interval = 30
End Sub
Works out of focus, but repetitively presses left arrow rather than press and hold arrow
Technique 3: keybd_event
Public Declare Sub mouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Sub timer_keyboardplayback_Tick() Handles timer_playback.Tick
Const keydown = &H1
Const keyup = &H2
Dim VK_LEFT = 37
keybd_event(VK_LEFT, 0, keydown, 0)
End Sub
Works out of focus, but still fails to press hold arrow
Can someone please show me how I can achieve a press & hold of the Left Arrow Key for several seconds, and then release.
The keybd_event and mouse_event functions are deprecated as of a few years ago. Instead you should use the SendInput() function.
Simulating input with it from .NET can sometimes be a bit tricky, fortunately though I've written a library called InputHelper (Download from GitHub) which is a wrapper around SendInput(). I've customized it so that it covers some of the many different ways of input handling and input simulation, mainly:
Simulate keystrokes (internally utilizing SendInput()).
Simulate mouse movement and mouse button clicks (also utilizing SendInput() internally).
Send virtual keystrokes and mouse clicks to the current/a specific window (internally utilizing Window Messages).
Create global, low-level mouse and keyboard hooks.
Unfortunately I've not yet had the time to write a proper documentation/wiki about this (apart from the XML documentation on each member of the library, which is shown by Visual Studio's IntelliSense), but so far you can find a little info about creating hooks on the project's wiki.
A short description of what this library consist of:
InputHelper.Hooks
For creating global, low-level mouse/keyboard hooks (utilizes SetWindowsHookEx() and other related methods). This is partially covered in the wiki.
InputHelper.Keyboard
For handling/simulating physical keyboard input (utilizes SendInput() and GetAsyncKeyState()).
InputHelper.Mouse
For handling/simulating physical mouse input (utilizes SendInput()).
InputHelper.WindowMessages
For handling/simulating virtual mouse/keyboard input, for instance to a specific window (utilizes SendMessage() and PostMessage()).
Sending a keystroke
Sending a "physical" keystroke can be done via two functions:
InputHelper.Keyboard.PressKey(Key As Keys, Optional HardwareKey As Boolean)
Sends two keystrokes (down and up) of the specified key.
If HardwareKey is set, the function will send the key's Scan Code instead of its Virtual Key Code (default is False).
InputHelper.Keyboard.SetKeyState(Key As Keys, KeyDown As Boolean, Optional HardwareKey As Boolean)
Sends a single keystroke of the specified key.
If KeyDown is True the key will be sent as a KEYDOWN event, otherwise KEYUP.
HardwareKey is the same as above.
You'd use the latter, since you want to control for how long you want the key to be held down.
Holding a key down for the specified amount of time
In order to do this you need to use some sort of timer, like you already do. However to make things a bit more dynamic I've written a function that'll let you specify which key to hold down, and also for how long.
'Lookup table for the currently held keys.
Private HeldKeys As New Dictionary(Of Keys, Tuple(Of Timer, Timer))
''' <summary>
''' Holds down (and repeats, if specified) the specified key for a certain amount of time.
''' Returns False if the specified key is already being held down.
''' </summary>
''' <param name="Key">The key to hold down.</param>
''' <param name="Time">The amount of time (in milliseconds) to hold the key down for.</param>
''' <param name="RepeatInterval">How often to repeat the key press (in milliseconds, -1 = do not repeat).</param>
''' <remarks></remarks>
Public Function HoldKeyFor(ByVal Key As Keys, ByVal Time As Integer, Optional ByVal RepeatInterval As Integer = -1) As Boolean
If HeldKeys.ContainsKey(Key) = True Then Return False
Dim WaitTimer As New Timer With {.Interval = Time}
Dim RepeatTimer As Timer = Nothing
If RepeatInterval > 0 Then
RepeatTimer = New Timer With {.Interval = RepeatInterval}
'Handler for the repeat timer's tick event.
AddHandler RepeatTimer.Tick, _
Sub(tsender As Object, te As EventArgs)
InputHelper.Keyboard.SetKeyState(Key, True) 'True = Key down.
End Sub
End If
'Handler for the wait timer's tick event.
AddHandler WaitTimer.Tick, _
Sub(tsender As Object, te As EventArgs)
InputHelper.Keyboard.SetKeyState(Key, False) 'False = Key up.
WaitTimer.Stop()
WaitTimer.Dispose()
If RepeatTimer IsNot Nothing Then
RepeatTimer.Stop()
RepeatTimer.Dispose()
End If
HeldKeys.Remove(Key)
End Sub
'Add the current key to our lookup table.
HeldKeys.Add(Key, New Tuple(Of Timer, Timer)(WaitTimer, RepeatTimer))
WaitTimer.Start()
If RepeatTimer IsNot Nothing Then RepeatTimer.Start()
'Initial key press.
InputHelper.Keyboard.SetKeyState(Key, True)
Return True
End Function
Example usage:
'Holds down 'A' for 5 seconds, repeating it every 50 milliseconds.
HoldKeyFor(Keys.A, 5000, 50)
Okay I'm new to VBA and the solution is probably very simple.
I have an Option Button (named Matrix) and if the button is checked I want the global variable rdb to get the value from the Cell (5,2).
This is my attempt:
Public rdb as Integer
Public Sub Matrix_Change()
If Matrix.Value = True Then rdb = Cells(5, 2).Value
End Sub
I noticed that this only works if I switch between two radiobuttons. This is perhaps because I selected "Change". How can I create a sub that only checks if the option button is checked? Simply removing the "_Change" part will lead to an error. Also is it possible to return the value from the Sub instead of using global variables?
Help is much appreciated!
(First Question) Choose the MouseUp Event instead of the Change Event
(Second Question) A Sub is unable to return a value
You can simply Replace Your Code to this:
Public rdb As Integer
Public Sub Matrix__MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Matrix.Value = True Then rdb = Cells(5, 2).Value
End Sub
I'm building a form Access with a load of TextBox controls on it. The GotFocus() event for every one of them will be exactly the same:
Private Sub Text1_GotFocus()
Text1.BorderColor = RGB(100, 100, 255)
...
End Sub
Private Sub Text2_GotFocus()
Text2.BorderColor = RGB(100, 100, 255)
...
End Sub
'... ad infinitum
This is, naturally, a maintanence nightmare and aesthetically a vast wad I have to keep scrolling past. I can throw BorderColor = RGB(100, 100, 255) etc into a function and have every handler call that function, but I'm still left with 3-line identical blocks for every single TextBox - throw in LostFocus and other events that are handled identically regardless of the TextBox, and it just becomes silly.
So the sensible thing to do would be to have a single AllTextBoxes_GotFocus() method, and have every TextBox's On Got Focus event point to that. Two problems though:
I cannot see any functions or subs I've defined in VBA in the drop-down in the form designer, only [Event Procedure] (which generates the standard Private Sub Text1_GotFocus() method) and any Macros in the application. Which is... odd. Considering Macros have a RunCode option for calling VBA functions, it seems a bizarre round-the-houses way of calling code, having to get the control to call the macro to call the code. Surely there's a better way (and I think Macros can only call functions in modules, not on forms).
I'm not sure how to get the sender, so I can set the appropriate control's border. VB.NET passes in the sender and event args in its events: Private Sub Text1_GotFocus(ByVal sender As System.Object, ByVal e As System.EventArgs), but VBA does not.
How can I handle mutliple events with a single handler, and get the sender of the event within the handler?
Here is how I do it but know this will overwrite any custom GotFocus events
Private Sub Form_Load()
On Error Resume Next
Dim ctrl As Control
For Each ctrl In Me.Form.Controls
If ctrl.ControlType = acTextBox Then
ctrl.GotFocus = "=changeColor('" & ctrl.name & "',100,100,255)"
'For LostFocus
ctrl.LostFocus = "=changeColor('" & ctrl.name & "')"
End If
Next ctrl
End Sub
Function changeColor(field As String, Optional red AS Integer =0 ,green AS Integer =0,blue As Integer = 0)
Me.Form.Controls(field).BorderColor = RGB(red, green, blue)
End Function
I have a datagridview and one of the columns is a Quantity column that should only allow integers. No negative symbols or decimal points. I have prevented the user from typing in any characters but they can paste them in. I could stop this in validation but I would ideally like to not even show characters that are pasted in. How would I detect and remove pasted in letters and in what event?
Ideally I would also like for only the paste to not work, so if the field already had a 2 in it and they pasted "test" then the 2 would remain, although that isn't as important.
Here's one approach:
'Set a flag to show when the form has finished initialising
Dim initialising As Boolean = True
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Form is initialised so set boolean to false
initialising = False
End Sub
Private Sub DataGridView1_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
'Only process once the form is initialised as values don't exist yet!
If Not initialising Then
If Not IsNothing(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value) Then
'If the clipboard contains text
If Clipboard.ContainsText Then
' Check to see if the value of the cell matches whats in the clipboard
If CStr(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value) = Clipboard.GetText Then
'You know its been pasted
If Not IsNumeric(CStr(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value)) Then
'This value should be rejected
Else
'This value is allowed
End If
End If
End If
End If
End If
End Sub
See my commentated code for an explanation.
I'm not sure you will want to do it on this event as it doesn't fire the event until the user leaves the cell. Hopefully however my approach of checking the value against what is in the clipboard might help you to identify if its a pasted value.
You'd have to implement a validating method and call it in some of the DataGridView's events (I'm thinking KeyDown/Keypress and MouseClick).
I think it's bad practice, because you'll be struggling to find more ways in which the user can trick your application; most apps nowadays let the user input whatever they want, but keep the user from completing her task until she has sanitized her input. Most also give clear on-screen instructions on how to do so.
Something like this
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
End Sub