Enter Key simulate click not working for checkboxes - vba

I have copied and slightly modified the code found at the bottom of this post to allow tabbing between form controls on a worksheet form I have created. Here is the segment relating to the KeyCode = vbKeyEnter:
Select Case KeyCode
Case vbKeyTab
Dim Backward As Boolean
Backward = (Shift And fmShiftMask)
Call MoveFocus(Cntrl:=Cntrl, TabIndex:=TabIndex, Backward:=Backward)
Case vbKeyEnter
Select Case TypeName(Cntrl)
Case TxtBoxType
If Not (Cntrl.EnterKeyBehavior And Cntrl.MultiLine) Then
Call MoveFocus(Cntrl:=Cntrl, TabIndex:=TabIndex)
End If
Case Else
On Error Resume Next
Application.Run Sheet1.CodeName & "." & Cntrl.name & "_Click"
On Error GoTo 0
End Select
End Select
The code works fine for tabbing forward and backward between controls. However, I'm having troubles using the enter key to check and uncheck check box controls. It works for option buttons, but does not work check boxes. If I add CheckBox1.Value = True to the CheckBox1_Click event it works to check it true, but naturally won't allow it to uncheck to false. I've tried adding the following IF statement to set it to true or false depending on current value, but then nothing happens.
If CheckBox1.Value = True Then
CheckBox1.Value = False
Else
CheckBox1.Value = True
End If
Another note: When I hit enter and nothing happens to the form control it moves the active cell down the active column. Any suggestions?

This may or may not be an answer to the programming question; I will let the voters decide.Q. Enter Key simulate click not working for checkboxes
Why would you do such a thing? You are spending time and effort to intentionally 'break' a working system. The spacebar has long been the accepted keystroke for toggling a checkbox on and off, Tab and Shift+Tab are used for navigating between form controls and Enter↵ is the default for OK.
By 'working system' I mean the whole Mac/Windows/Linux GUI system and the millions of people that have trained to work within long defined standard practises. You can take the mouse away from any competent office worker and their productivity will not suffer. They can navigate through the system and applications to perform their work because everything in the system from setting the video resolution to typing workers' timecard data into a custom form works the same way.
Take a Windows user and stick them in front of their first Mac. They can use the programs and get the job done because the vast majority of operations (and particularly the ones for basic operation of an application) are the same. Take a Mac user and stick them in front of a Linux machine and you will get the same results. The basic day-to-day operation of the machine and its programs are the same.
What is the point of any prospective applicant looking for employment to come with a CV filled with office experience if you are going to create a proprietary system that takes all of their training, practise and experience and throws it out the window?
The use of the spacebar to toggle a checkbox on and off predates Windows 3.0. There is a difference between building a better mousetrap and reinventing the wheel¹.
¹ If you don't think that maintaining GUI standards is something to strive for, consider the backlash that the Office 2007 ribbon created. An entire sub-industry dedicated to producing add-ons that brought back Office 2003 functionality was spawned.

Related

How to check if VBA application is in focus when GetAsyncKeyState is pressed

I have a long running application written in VBA. Because it runs for a long time I call a sub from the main loop every iteration to check if the user has pressed the escape key. The sub is listed below. The code works great except that it is always listening, even when the VBA application does not have the focus. What's the best way to approach this? Is there an alternative to GetAsyncKeyState which only listens when the correct application is in focus, or are there system calls I can use to check that the correct window is in focus.
Private Sub checkForUserEscKeyAbort()
'Listen for escape key and exit gracefully if user aborts.
Dim abortResult As VbMsgBoxResult
If GetAsyncKeyState(vbKeyEscape) Then
abortResult = MsgBox("Escape key pressed, do you want to abort?", vbYesNo)
If abortResult = vbYes Then Call teardownSLICER
End If
End Sub
The problem is the way GetAsyncKeyState operates. It does more than just check it the key is currently down, it also checks if the key was pressed since the last time GetAsyncKeyState was called. Thus, your problem of "always listening".
You could use GetKeyState, but frankly, I'm not a fan of that option either. Your code must be well crafted to avoid a polling window that is so small that you literally have to hold the key down to ensure it isn't missed.
A viable alternative is key masking where you use a combination of keys such as Shift-Escape. There is a decent introduction to this (and many other subjects) on Chip Pearson's site. However, this is still not my preferred method.
My preference has already mentioned in the comments. Your application may best be improved with a user form. It also gives you the ability to get information in front of the user. Perhaps they wouldn't try quitting the application if a progress bar on the user form indicated 95% completion. Maybe you can add a pause button that free's some resources for a prescient need and then resumes when the user is ready. That little extra functionality on its own is enough to win me over but there is an even better reason for using a User Form - it does exactly what you asked!
User forms, and many user form controls, have Keydown, Keyup, and Keypress events that only trigger when the form (or control) have focus. This is precisely what you wanted.

Excel Add-In - Alternative to ActiveMenuBar.Reset

Problem: After an upgrade from Office 2010 to 2013, the Essbase Excel Add-in ("essexcln.xll", which Oracle ended support for in 2013) causes the focus to always return to a window with an active connection, when there is more than 1 window open. If the Essbase Add-In is loaded at startup, Excel also freezes. Note that Smartview has replaced this Add-In but for other reasons I need to continue using it. I can manually go to File > Options > Add-ins > Manage Excel Add-Ins and manually check/un-check when Essbase is causing these errors, but I'd rather do that with a quick keyboard shortcut.
Workaround: Create a custom add-in to quickly toggle the Essbase Add-In installed property to load and unload it. Maybe an add-in is overkill - but I don't really use a PERSONAL.XLSB, and I'd like this functionality to be available at all times.
Problem 2: When the Essbase add-in is unloaded, the "Add-in" Menu bar still shows a custom command: "About Oracle Essbase Spreadsheet Add-in." "Essexcln.xll" is notoriously buggy, and this "About Oracle Essbase Spreadsheet Add-in" can linger on even when the add-in is unchecked manually. My solution is to use ActiveMenuBar.Reset - only after I've unloaded the add-in. I don't want to reset if I've just toggled Installed to True.
Is there an alternative to ActiveMenuBar.Reset? This feels like a hack - kind of like using ActiveCell or ActiveSheet - but I don't want to manually check/un-check the Addin, which may or may not clear the "About Essbase..." anyway.
Notes: Yes, maybe it's not the most efficient to loop through Add-ins, but there's so few of them that I don't really care. I'd much rather avoid using ActiveMenuBar.
Sub Toggle_Essbase_AddIn()
Dim x As AddIn
Dim installed As Boolean
For Each x In Application.AddIns
If x.Name = "essexcln.xll" Then
' Get initial installed status
installed = x.installed
' Toggle
x.installed = Not x.installed
' Reset menu bar if Essbase was initially installed
If installed Then
ActiveMenuBar.Reset
End If
Exit Sub
End If
Next x
End Sub
As it stands, your code is incredibly inefficient, to the point that it's tough to explain why. Looking at the core of your procedure (after removing wasted space) we have:
Sub Toggle_Essbase_AddIn()
Dim x As AddIn, installed As Boolean
If x.Name = "essexcln.xll" Then
installed = x.installed
x.installed = Not x.installed
If installed Then
ActiveMenuBar.Reset
End If
Exit Sub
End If
End Sub
First off, this line does absolutely nothing:
x.installed = Not x.installed
...since it's after the value of installed has been set.
I can only assume that your intention was to toggle the value that's being passed to variable installed?
It will save you a lot of confusion if you get a little more creative with your variable names, instead of risking a word that, for all intensive purposes, could be considered Reserved. Without picking apart semantics, it is best not to name your variables with words that VBA wants to use for other things, and this is a perfect example why. Even if VBA doesn't get confused, you might, and you have an almost infinite number of other, more meaningful names to pick from.
Assuming that the intention was to toggle the value that's being passed to variable installed then you could have gone:
If x.Name = "essexcln.xll" Then
installed = not x.installed
If installed Then
ActiveMenuBar.Reset
End If
Exit Sub
End If
Preferably you should use Exit commands sparingly, or not at all -- only when there isn't an option to leave the loop/sub/if "naturally" but I'm not going into that now since it's irrelevant in a moment.
...or better yet:
installed = Not ( x.Name = "essexcln.xll" )
If installed Then
ActiveMenuBar.Reset
End If
...or even shorter:
If Not ( x.Name = "essexcln.xll" ) then ActiveMenuBar.Reset
...so now your whole sub would be:
Sub Toggle_Essbase_AddIn()
Dim x As AddIn
Dim installed As Boolean
For Each x In Application.AddIns
If Not ( x.Name = "essexcln.xll" ) then ActiveMenuBar.Reset
Next x
End Sub
...but you're still being unnecessarily inefficient.
There is no reason to loop through the AddIns. A quick Google of Application.Addins shows that it's easiest to refer to the Add-In by it's name.
I've never used Essex, but another quick Google to find the add-in documentation tells me that the name of the add-in is "Oracle Essex".
Therefore, one line replaces your entire procedure:
If Not AddIns("Oracle Essbase").Installed Then ActiveMenuBar.Reset
(or I'm not sure if it was intended to NOT be NOT because your code was unclear) - but this is far more efficient and does the equivalent of your code - as long as you're sure the add-in actually exists (not the same as installed, Google it), then this does the same thing as your entire procedure.
If you're not sure if the add-in exists, then Google saves the day again with a link to a Stack Overflow question.
Since I don't have the add-in and you haven't included any specific example of the problem, I can't say for sure if this answers the question completely, but I assure you that taking some extra time on one's own due diligence in coding will safe a lot of effort for you - and others - in the end.
Heads up, I had a lot to write here to get to "one line" so I can't guarantee there were no oversights, but the lesson here is more about research-before-coding.
I suggest you study the documentation about add-ins and toolbars at MSDN as well as documentation specific to the 3rd party add-in at Oracle (via the links above, and sub-links from those pages) and I am confident that with some effort your solution will become clear.
If not, I suggest you add more information to your question. ...and please, don't attempt to write any "add-ins for add-ins"!
Good Luck (and welcome to Stack Overflow!)
...and after all that, I notice that you added a link to an image instead of adding an image, so it would have been more noticeable. I can't tell without clicking the toolbar but I'm pretty sure that's not a menu bar command. That's a ribbon group, and it's empty.

Win Forms VB App in Win 10 - focus issues

I have an app built in VB in Visual Studio 2012 that works absolutely fine on my windows 10 desktop, but as soon as i use it on my windows 10 tablet i see a couple of issues :
1) any forms that were intended to be smaller than full screen are shown fullscreen anyway (almost as if you're not allowed any windows less than full screen) i can live with that if need be, but surely theres a way around it?
2) - the most important one - for some strange reason, i start my app, and when i click on a button let's say it opens form 6. once i finish what i'm doing the code closes the form 6, but the previous form is now hidden and all you see is the desktop. ie it's still running, it just lost its focus and must be selected again. I understand i could set the focus to the underlying form before closing form 6, but here's the problem : it can be one of several different forms calling form 6..... so how do i make the app stay aware of what form called form6 so that when done i can return focus there?
this doesnt seem to be a problem on the desktop so i've never encountered it before. i hope one of you experts has dealt with this before
I don't have enough points yet to comment. My guess for #1 is either resolution or DPI differences between the 2 screens, and I don't have experience with the latter.
As for #2 and keeping the form aware of who called it, this is how I do it, which may not be the best solution, of course. ;)
First any form that can have multiple callers has a variable defined like
Dim callingForm As New Form
I typically create an Initialize routine to handle as much as possible before loading a form and this routine is called with the parent form (Me) as a parameter.
Dim frm As New frmClient
frm.Initialize(Me)
frm.Show()
Me.Hide()
In Initialize, callingForm is set to the parent
Public Sub Initialize(parent As Form)
callingForm = parent
'whatever else you need to do to init...
End Sub
Then when you exit the form...
Private Sub exitForm()
'whatever other closing stuff you need to do...
callingForm.Show()
Me.Close()
End Sub
That should get you started.
fyi for anyone encountering this problem, i solved it myself and it didn't require any coding. It was a difference between how vb apps act when the tablet is in "TABLET" mode, and when tablet mode is switched off. So all that needed doing is switching off enhanced tablet mode in the windows 10 settings

SendKeys to click on a dialog box button in Access

I am using Access 2013. I searched a variety of online resources. I thought SendKeys was the answer.
Once per quarter, my client will receive an updated Access database. All table names should be identical each quarter. I want them to run a macro, specify the location of the new file, and then the macro updates the linked tables and executes all other queries I’ve built (I have the last part working).
The part I have not be able to get working is to check the “Always prompt for a new location box”, check the “select all” box and click OK (and then click OK and close after the client specifies the new file location). Below is the code I am using.
Function Open_LinkedTableManager()
DoCmd.RunCommand acCmdLinkedTableManager 'this step works fine
'the following lines, up until Application.Run don’t appear to be
'doing anything. The code will run, but I have to manually execute
'each of the steps I am trying to automate before it gets to the
'Application.Run step
SendKeys "%a", True ' also tried SendKeys "%(a)" and "+a", "a", etc,
'True; Alt+a checks the "Always prompt for a new location box”
SendKeys "%s", True ' also tried SendKeys "%(s)", True; Alt+s checks the "select all" 'box
SendKeys "{Enter}" ' then user specifies location of new file
SendKeys "{Enter}" ' click OK after receiving message "All selected linked tables 'were successfully refreshed"
' click Close to close linked table manager and proceed to the next step below (not 'sure how to do this)
Application.Run ("Update_all_queries") ' this is working;
End Sub
If sending to yourself then try DoEvents after each sendkey.
DoEvents Function
Yields execution so that the operating system can process other events.
Syntax
DoEvents( )
Remarks
The DoEvents function returns an Integer representing the number of open forms in stand-alone versions of Visual Basic, such as Visual Basic, Professional Edition. DoEvents returns zero in all other applications.
DoEvents passes control to the operating system. Control is returned after the operating system has finished processing the events in its queue and all keys in the SendKeys queue have been sent.
DoEvents is most useful for simple things like allowing a user to cancel a process after it has started, for example a search for a file. For long-running processes, yielding the processor is better accomplished by using a Timer or delegating the task to an ActiveX EXE component.. In the latter case, the task can continue completely independent of your application, and the operating system takes case of multitasking and time slicing.
Caution Any time you temporarily yield the processor within an event procedure, make sure the procedure is not executed again from a different part of your code before the first call returns; this could cause unpredictable results. In addition, do not use DoEvents if other applications could possibly interact with your procedure in unforeseen ways during the time you have yielded control.
I have solved your dilemma. All I needed to do was place the sendkey statements before the call to to the linked tabled manager. See Below - Worked Great For Me! I was also able to add all of the commands in your order and they worked great. Good luck, hope this helped. Let me know. Adam
PS: If you have many tables to change the path on, this will be painful for the user for every table you are forcing them to have to set the path for.
SendKeys "%s", 1
DoCmd.RunCommand acCmdLinkedTableManager

VBA Status Bar

I am working on a Word VBA macro app for 80 or so users. The office has high staff turnover, so training suffers, and so one of the self imposed requirements for this project is comprehensive, friendly documentation. However, to supplement this, and to save newbies having to open up a 100 page document when they want to try something new, I want a status bar on every userform (there are five) that provides contextual help. I find tooltips annoying.
I don't have a lot of experience, so I was wanting to
Essentially, I have a file containing every status string. (This is currently a text file, but I was wondering if I should use a spreadsheet or csv for ease of editing by other staff in future.) Every control has a MouseMove event which refers to a function: getStatus(cID) that opens the file, grabs the line and displays it in the status label. It also grabs a few parameters from the same line in the file, such as whether the label is clickable (to link to a page in the help file), and what colour the label should be.
So a few questions really:
Will the application be slow if a userform is constantly referring to a file? It feels fine to me, but I've been in it far too long, and I'm the only user accessing that file. There will be 80 constantly accessing it.
Is MouseMove over a control the best way? Should I instead use co-ordinates?
Most importantly (in terms of me having to do as little work as possible) is there some way to do this so that I do not have to have a MouseMove event on every single control? I have a good few hundred or so controls, each with their own identifier (well, not yet, but they will if this is the only way to do it). Maybe when the form loads I could load ALL the possible status lines so they're ready for whenever the control is moused over. But then, maybe the loading time is negligible?
Appreciate any ideas or thoughts - especially if VBA already has a whole range of functions to do this already and I'm just trying to reinvent the wheel. I can't use the application status bar, because the user rarely sees the application itself.
Thanks!
EDIT:
It is for both data entry, clicking around and a bit of document generation.
It is a controlled environment so macro security issues aren't a big concern for me - and if something goes wrong it's someone else's fault or problem :)
Is this data entry app or do they just click stuff? Because often the field with focus is different to the item the mouse is hovering over, this can cause a lot of confusion.
Constantly reading from a file is a huge waste of time and resources - it is much better to load them only once into an array or collection when the form is loaded.
On MouseMouse event is better than coordinates because you can move things around without worrying. It's a lot of code but you should be able to generate most of that if you have a list of control names because the code should be identical.
ie
Sub Control_MouseMove()
DisplayStatus(Control)
End sub
I would consider the StatusText property and ControlTipText property of controls for this kind of help.
StatusText
This example sets the status bar help text for the form field named "Age."
With ActiveDocument.FormFields("Age")
.OwnStatus = True
.StatusText = "Type your current age."
End With
ControlTipText
This can be assigned from the property sheet for the control.
Private Sub UserForm_Initialize()
MultiPage1.Page1.ControlTipText = "Here in page 1"
MultiPage1.Page2.ControlTipText = "Now in page 2"
CommandButton1.ControlTipText = "And now here's"
CommandButton2.ControlTipText = "a tip from"
CommandButton3.ControlTipText = "your controls!"
End Sub