How to turn on 'Snap To Grid' on Excel start up with an add-in - vba

I am looking for a way to automatically turn on Snap to Grid every time I start Excel.
I worked out this code and put it in an Add-In (.xlam) that I always load on Excel start-up.
Private Sub Workbook_Open()
Dim cbc As CommandBarControl
Set cbc = Application.CommandBars.FindControl(ID:=549)
If Not cbc.Enabled Then cbc.Execute
End Sub
But when I start Excel, it throws this error at cbc.Execute:
Can anyone tell me what is wrong?

I think you have two problems:
Problem 1
I believe that what you are trying to do with the Enabled property is to check the toggle state of the control. Instead, I think you want the State property instead (which doesn't show in intellisense). Your code should be something like this:
Public Sub ActivateSnapToGrid()
Dim cbc As CommandBarControl
Set cbc = Application.CommandBars.FindControl(ID:=549)
If Not cbc Is Nothing Then
If cbc.Enabled Then
'if snap to grid is off...
If cbc.State = 0 Then
cbc.Execute
'State should now = -1
End If
End If
End If
End Sub
Problem 2
The 'Snap to grid' control is not enabled if there is no workbook present (check this by closing all workbooks). In its current state, your code tries to execute in this case i.e. If Not cbc.Enabled Then cbc.Execute because I think you are trying to check if it is 'on' not if it is enabled.
Because an Excel add-in will load as a 'hidden' workbook, I don't believe it would enable the 'Snap to Grid' command bar control. Therefore, in the Workbook_Open event of the add-in then the control will be disabled and that's why you get the error.
You need an application-level event handler in your add-in. This is very common for Excel add-ins. See here on MSDN for some explanation. Plus also see this article by Chip Pearson which is very useful. It will allow you to write an event for any Workbook_Open event generated after your add-in loads.
So you will end up with this code (per Chip Pearson) in your add-in to call the sub I presented above (in Problem 1 section):
Private WithEvents App As Application
Private Sub Workbook_Open()
Set App = Application
End Sub
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
Debug.Print "New Workbook: " & Wb.Name
Call ActivateSnapToGrid
End Sub
I haven't tested this thoroughly as it can't be known how your wrote your add-in. However, this is a very (if not the) standard pattern for doing this kind of stuff. See MSDN and the Chip Pearson article and you will figure out which code goes in a Class, in a Module etc.

Related

Workbook.Activate method acting strangely

I have an Excel add-in running a procedure that displays an OKOnly MsgBox if a certain criteria is not met, and attempts to close a userform, activate a specific workbook, and terminate code execution. In the UserForm_Terminate() event I have:
Private Sub UserForm_Terminate()
Debug.Print ActiveWorkbook.Name
Application.ScreenUpdating = True
wbk.Activate
sht.Activate
Debug.Print ActiveWorkbook.Name
End
End Sub
If I begin running the procedure with a new blank workbook active, that workbook is still the active workbook when code terminates, but both print statements above indicate that the target wbk is actually active. However if I use a breakpoint and step through wbk.Activate, the target wbk is activated as expected. The workbook and worksheet objects are both available and there is no error. Any ideas why Workbook.Activate is not behaving as expected during execution? I expected turning screenupdating on would solve my issue but no dice.
#barX He's not relying on ActiceWorkbook for code, he's just checking it to see if it's working ....
BTW Welcome to the Excel 2013 SDI bug!
Maybe following the proper way of initializing/using a Userform will help. See Mathieu's RubberDuck article on Userform.Show
On a related note, maybe changing the Parent Window Handle for the form before closing might work as well. See Keeping Userforms On Top Of SDI Windows In Excel 2013 And Up
I'm not sure if the SDI bug is my issue but it did lead me to an answer (thanks #Profex). I was not able to reproduce that bug, and there is also another path in my procedure that ends with the same Userform_Terminate() event and does reactivate the target wbk, though I can't determine what is causing the differing functionality. Nonetheless, one solution to that issue was to hide and then show the active window and that suffices in this case, though probably not ideal in many situations:
Private Sub UserForm_Terminate()
Dim win As Window
wbk.Activate
sht.Activate
Set win = Application.ActiveWindow
win.visible = False
win.visible = True
End
End Sub

Cannot close workbook after running sub with userform

I have a macro that opens a userform to capture a start and end date. After clicking OK on the userform, a file dialog box opens to select an Excel Workbook to open.
Immediately after I run the below sub, I can't close the workbook that is opened by using the 'X' in the top-right corner. I also can't save the workbook by clicking the save icon.
However, if I click on another workbook or switch to a different sheet in the workbook that was opened, and then click back to the one opened by the sub everything works as it's supposed to.
Also, I replace the userform with two input boxes, to capture each of the two dates, I am able to close the workbook that is opened with no issue.
Maybe there's something funny with the userform code?
This is all that is in the userform.
Private Sub Ok_button_Click()
call module1.forecast
unload userform1
end Sub
And this is the main sub.
Sub forecast()
dim start_SFY as long
dim end_SFY as long
dim filesToOpen as object
dim wb as workbook
Application.ScreenUpdating= False
start_SFY = userform1.textbox1.value
end_SFY = userform1.textbox2.value
set filesToOpen = application.fileDialog(msoFileDialogOpen)
filesToOpen.show
set wb = application.workbooks.open(filesToOpen.selecteditems(1),false)
Application.ScreenUpdating= True
End Sub
Here's the sub showing userform1
Sub run_userform()
userform1.show
End Sub
Also, here is the Excel version:
Excel 2013 64-bit (15.04753.1003) Part of Microsoft Office 365 ProPlus
Can someone maybe try to replicate the issue that I'm having? I'm wondering if this is an issue related to my employer's version of Excel or something?
This sort of thing has never happened to me before.
Also, I can close the program with VBA. It's just when trying to click the 'X' that it won't close.
Update:
I was able to get the code, with no changes, to work fine at home on Excel 2016. I'm going to get a coworker to test on their system today.
When I was home, I didn't put a button to call the sub on a worksheet. I called it from the VBA editor. After some testing this morning, it seems that the button is the issue. If I call the sub from the VBA editor, I can close the opened workbook. However, if I use a command button (form control, not ActiveX as I get an error saying, "Cannot draw object" whenever I try to add any kind of ActiveX object to a worksheet) the opened workbook will not close.
I think I have found the problem
This issue seems to be with the 'form control command button'. ActiveX was disabled in the Trust Center. When I enabled it and created a command button, I was able to close the opened workbook. I then tried the command form button again, and could not close the opened workbook. I was also successfully able to close the opened workbook when I ran the sub from the sub listbox in the developer tab, and when I place the sub in the Excel Ribbon and ran it from there.
Any idea as to why the control form command button would cause this issue?
The root of the problem is the change to using multiple excel interfaces in 2013. Microsoft addresses the issue in the Solutions for SDI Issues section of this page.
A workbook cannot be closed by clicking the red "X" Close button when
that workbook is programmatically opened via a modal user form. To
work around this issue, it is suggested that you add the following
code to the user form Layout event procedure and then open the user
form as modeless
Private Sub UserForm_Layout()
Static fSetModal As Boolean
If fSetModal = False Then
fSetModal = True
Me.Hide
Me.Show 1
End If
End Sub
Another option is to open the workbook window, activate any other
window, and then reactivate the workbook window. You should now be
able to close the workbook using the Close button.
So these are the two options they present and one I came up with.
Option 1) You can switch your dialog to modeless using their code, or by setting the ShowModel property to false in your userform.
Option 2) As you discovered, manually switching between workbooks after opening via modal userform resyncs everything. Not a good solution for the users of my code, and I don't recommend relying on it.
Option 3) It's worth mentioning, if you don't open the file via the userform then there's no issue. So if last thing the userform needs to do is open the file, you can easily save the file path in a string, unload the troublesome userform and move the workbooks.open call after closing. Here's an example of what I mean
Public EDIT_FILE_DIRECTORY As String
Public Sub Main()
fileOpenerForm.Show
If EDIT_FILE_DIRECTORY <> "" Then
Call Workbooks.Open(EDIT_FILE_DIRECTORY)
End If
End Sub
And in the userform something along these lines, where the filename is created based on userform parameters and a listbox selection:
Private Sub OpenSelectedWorkbooks_Button_Click()
Dim workbookName As String
workbookName = selectionList.Item(Me.FileSelection_ListBox.ListIndex + 1)
EDIT_FILE_DIRECTORY = ROOT_DIR & GetSelectedSubfolder & "\" & workbookName
Unload Me
End Sub
Try fileOpenerForm Show 0 to open open it with Modal = False (Makes the macro run while the Userform is visible)
Do not forget to add fileOpenerForm.Hide later on.
Also Load fileOpenerForm and Unload fileOpenerForm may be useful

Excel 2013 to 2010 backward compatibility issue with ActiveX (NOT Dec14th update issue)

I am transitioning a set of interrelated Excel documents that use VBA code for lookups, data manipulation and calculations from Excel 2010 to Excel 365. I have both on my development machine, however these get sent (via email) to customers all over and then returned. I use .xlmb file formats for the file size savings however this doesn't seem to be affecting the outcome.
CURRENT ISSUE
When I save a workbook using 365 on my development machine, users get errors when performing an action that runs my VBA code when run from a machine that only has 2010 installed (if both versions are installed, the behavior does not seem to happen). I have focused the problem to when there is code in a module and there is an ActiveX control on a sheet. ONLY this combination seems to create the issue.
The test file/code I've created that consistently shows the issue is a workbook with the following code in a module:
Dim strBook As String
' Worksheet Names
Public Const wksTest = "Sheet1"
Public Function TestMe(PassedSheet As String)
strBook = ActiveWorkbook.Name
Workbooks(strBook).Worksheets(PassedSheet).Protect
Workbooks(strBook).Worksheets(PassedSheet).Unprotect
MsgBox "Worked from function", vbOKOnly, "Response"
End Function
And code in the sheet. It works with this code and selecting cell B2 protects and then unprotects the sheet while displaying message boxes:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Row = 2 And Target.Column = 2 Then
strBook = ActiveWorkbook.Name
Me.Protect
Me.Unprotect
MsgBox "Worked from sheet", vbOKOnly, "Response"
Call TestMe(wksTest)
End If
End Sub
However if an ActiveX Command Button named "TestButton" is added to Sheet1 and this code is added:
Private Sub TestButton_Click()
strBook = ActiveWorkbook.Name
Me.Protect
Me.Unprotect
MsgBox "Worked from Button", vbOKOnly, "Response"
Call TestMe(wksTest)
End Sub
The workbook will no longer function if saved from 365 and opened with 2010 on a machine with only 2010 installed.
This is unique enough that it has been difficult to test. Currently, the only option I seem to have is replace all my command buttons with Form Controls. This also is a separate issue from the security update (which is really muddying the waters).
I would love to get feedback on possible fixes for this issue or even just confirmation from others that this is an Excel issue and not somehow limited to our installation.
Thanks
I was looking for help on a similar question and stumbled on this info on the Microsoft help site. If this doesn't solve the problem you have, it might be a step in the right direction:
"If a workbook contains ActiveX controls that are considered to be Unsafe for Initialization (UFI), they are lost when you save the workbook to an earlier Excel file format. You may want to mark those controls as Safe for Initialization (SFI).
What to do: If you open a workbook that contains uninitialized ActiveX controls, and the workbook is set to high security, you must first use the Message Bar to enable them before they can be initialized."

Code to run AFTER right click

I want to block out some of the command bar options, but the method I am currently using is a bit too restrictive. I currently use this code:
Sub Auto_Open()
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
Sub Auto_Close()
Application.CommandBars.FindControl(ID:=847).Enabled = True
End Sub
The problem with this is that as long as the workbook that has this code stays open no other workbook can use voided controls. I think a solution might be to use:
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
This will stop the user from using my voided controls when they right click, say to delete a sheet.
I now need a way to remove those voids as soon as they click away, so the controls are available to use in any other workbooks they may have open. Is there an event that I can use for this which isn't so common that it will make the code run every 10 seconds? I also wondered whether you can use delays in vba to run a piece of code 5 seconds after a previous piece of code?
In the workbook module in which you want to disable the command bar options enter:
Private Sub Workbook_Activate()
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
Private Sub Workbook_Deactivate()
Application.CommandBars.FindControl(ID:=847).Enabled = True
End Sub
Whenever you are working in the workbook the commands should be disabled. I'm not certain it will work in Excel 2007 because Application.CommandBars.FindControl(ID:=847).Caption is &Delete and Delete is still enabled on both the cell right click and tab right click.
In Excel 2007 you can disable Ribbon commands for the active workbook but it's not easy. You have to change the workbook extension to .zip then edit the .rels file in _rels folder within the zip. Change the extension back to what it was after editing and the targeted commands or groups should be disabled. Disabling the Ribbon command also disables any associated right-click commands. The kicker is that sheet add/delete commands aren't available in the Ribbon.

Application.Quit in UserForm attempts to run rest of macro before exiting

My question is: using VBA in Excel 2013 how can I gracefully close an entire instance of Excel when the user decides they don't want to fill out a UserForm and clicks quit or cancel?
Currently, if the user clicks quit or cancel, I check to see if my instance is the only one open. If it is not, I can use ThisWorkbook.Close and I think I will be okay. However, if it is, I do not want the application to still be present, so I used Application.Quit. This, though, tries to finish running the macro, throws errors (originally "type mismatch" because I unload the form), and only closes after I click "Debug" or "End" (which it does so fast for either I cannot actually debug). I'm ignoring the first case for now and just trying to exit the entire application. It's a very long macro with a lot of subroutines and functions, so for debugging and posting here, I have shortened it. The type mismatch error no longer occurs, but I believe that was a consequence of the actual error: code running after the command to close the application is called.
First, here's the code that starts everything:
Private Sub CommandButton1_Click()
Call form_variables
frm_REQUEST.Show
Call a_REQUEST_main
End Sub
The subroutine
form_variables
is a subroutine that creates public variables so I can store the data from the UserForm.
frm_REQUEST.Show
initializes (including calling a function that finds another workbook, extracts a list, does some formatting, closes the workbook and enters the list into the userforms drop down box) and shows the form, and finally
a_REQUEST_main
uses the public variables (where UserForm data is stored) and does its thing (but shouldn't do anything if the UserForm is closed).
The code that is executed when .Show is called is:
Private Sub UserForm_Initialize()
' Get job numbers from other workbook
Dim job_selection_list As Variant
job_selection_list = get_job_list()
With frm_REQUEST.Job_Number_ComboBox
.List = job_selection_list
End With
' set focus on Job Numbers
JN_combobox.SetFocus
End Sub
Private Sub cancel_button_Click()
Set job_selection_list = Nothing
Unload Me
Application.Quit
End Sub
Private Sub submit_button_Click()
' Values from userform saved as global (?) variables so other subroutines can access.
End Sub
I stepped through the program and saw that, once Application.Quit is called in the UserForm, the macro, in the main subroutine, steps to
Call a_REQUEST_main
but it should really just close everything out. I tried doing "save" commands, and changing the order of things, and read about objects needing to be set to nothing (hence the setting of the job_selection_list which is created when the drop down list is initialized), but I cannot seem to get this to work, or find anything online. Can anyone provide some guidance or let me know of a better way to close an excel instance? Help me Stack-Overflow Kenobi, you're my only hope!
Thanks.
Just add a variable to account for when the user closes the form
in the form
'hold flag if users cancels form
Public btnCancel As Boolean
Private Sub CommandButton1_Click()
Unload Me
btnCancel = True
End Sub
'set the flag each time the form active
Private Sub UserForm_Activate()
btnCancel = False
End Sub
then in your code
Call form_variables
frm_REQUEST.Show
If frm_REQUEST.btnCancel Then
Application.Quit
Else
Call a_REQUEST_main
End If
Put Application.Quit in the form's Terminate event handler instead of in the button's Click event handler.
The reason is that clearly the procedure will keep running even if the form has unloaded. So use the events to your advantage.
Putting it in the Click event will unload the form, but the procedure will keep running which of course may raise errors or other undesired effects.
Note: You may be prompted to save/discard changes (if any) to the workbook.