VBA Button Application.Caller returning wrong row - excel

I try to use the following command on several buttons in different worksheets of myexcel workbook.
MsgBox (ActiveSheet.Shapes(Application.Caller).TopLeftCell.row)
When copying those buttons to another area of my workbook, I sometimes have the problem that still the row number of the original button location is displayed. As example in the screenshot below, I click the button at the bottom, but it return row 705 instead of 739. Can anyone explain this behaviour?

Make sure all your buttons have unique names. Somtimes it can happen that copied buttons have the same name (due to a bug in Excel). Then VBA cannot distinguish them and uses the first one that it finds. Check all your button names and make sure they are unique.
This issue can be reproduced easily:
Open a new workbook
Add a button (FormControl)
Name it MyButton
Copy that button
Paste it somewhere else in the same sheet
Use the code from the question for both buttons
The button will now always show the row of the first button, because both have the exact same name.
Image 1: Illustration how this issue can occur. In the end both buttons show the same row value. Because they have the same name VBA only can find the first one.
The soulution is, when ever you copy a button immediately make sure you rename it to a unique name.

It never happened to me to copy a shape with the same name. Only its Caption remained... But, since people says it is possible, try assigning this code to all the shapes in discussion, please. If the shape double name would be the reason, the code will return twice:
Sub callButName()
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
If sh.Name = Application.Caller Then MsgBox sh.TopLeftCell.Address
Next
End Sub

Related

Rowsource not working via VBA

I can't get the RowSource property of a list box to update via VBA. From another thread, I found the syntax, so I think this is correct. But, despite not failing, it doesn't do anything to the RowSource property (it remains blank). Below:
frmAddIngredient is the user form.
lbxIngredient is a listbox control in that form.
UniqueIngredients is one of the sheets in the workbook.
NumberOfItems is 1 (in this case).
It doesn't give an error, but it doesn't change anything, either. The form itself is not active at this time. This code is supposed to set up the form for later showing.
frmAddIngredient.lbxIngredient.RowSource = Sheets("UniqueIngredients").Range("A1:A" & CStr(NumberOfItems)).Address
The most recent code is
frmAddIngredient.lbxIngredient.RowSource = "=UniqueIngredients!A1:A1"
but, it still doesn't change anything in the actual form.
Also, can I add a new post, or do I have to continue editing this one and adding stuff?
What you want (as discussed in the comments on your question) is not possible. Setting something by code does not change it's property in the properties window and is only so until your project resets.
Consider a Userform with 2 buttons, with their original name and caption and then in a module paste these 2 subs.
Sub demo1()
UserForm1.CommandButton1.Caption = "Demo 1"
UserForm1.Show
End Sub
Sub demo2()
UserForm1.CommandButton2.Caption = "Demo 2"
UserForm1.Show
End Sub
When you run the first Sub demo1 Button 1's caption has changed but Button 2's caption has not.
Close the Userfom and now run demo2, you'll see that Button 1's caption is back to it's original hard set (properties window) name and that now Button 2 has a different name.

I want to set the print area of a sheet to nothing without having to hiding the sheet [duplicate]

Is there any way in Excel to make it so that a particular tab isn't included when you print the entire workbook? As in, Sheet1, Sheet2, and Sheet3 print, but not Sheet4.
So like when someone goes to File -> Print or whatever, and hit OK, etc, everything prints out except for a particular designated tab (which I assume I have to "hide from printing" somehow).
I'm sending this workout out to a bunch of people so I'm trying to avoid forcing them to enable/run macros and all that stuff. Is there just a property of a sheet somewhere like "exclude from print"?
These answers will require the use of macros.
Hide the sheet before printing.
sheets("WorksheetToNotPrint").visible = xlSheetHidden 'or 0
or
sheets("WorksheetToNotPrint").visible = xlSheetVeryHidden 'or 2
'used if you don't want people to unhide the worksheet without knowing code
and
sheets("WorksheetToNotPrint").visible = xlSheetVisible 'or -1
when done
another option is to print the sheets you want printed (may be useful if you only want a few sheets printed:
Sub Print_Specific_Sheets()
Sheets(Array("Sheet1", "Sheet2", "Sheet4")).Select
ActiveWindow.SelectedSheets.PrintOut
Sheets("Sheet1").Select
End Sub
Actually the answer seems to be surprisingly simple.
If you hide the sheet, it will not be printed when you print the entire workbook.
You won't need a macro, just
right click on the sheet name
choose hide
If needed you or other users can unhide it as well.
If you use the BeforePrint event then you can alter what the built in print function can do. The BeforePrint macro is inside 'ThisWorkbook' under 'Workbook'.
For example you can hide sheet 4 so it doesn't print like this:
Private Sub aWorkbook_BeforePrint(Cancel As Boolean)
Sheets(4).Visible = xlSheetHidden
End Sub
The downside to this method is there is no AfterPrint method to unhide the sheets. So you need to circumvent the print dialog. There are several ways to do this but here is one very simplistic method. (Note: This might not be a good solution if you have many other printing scenarios that you need to account for)
Here I am telling exactly what sheets to print (only sheet 1 and 4). You can use names instead of numbers and you can even create a loop and look for variables to determine what sheets you want to print.
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Application.EnableEvents = False
Sheets(Array("1", "4")).PrintOut , , 1
Application.EnableEvents = True
'//prevent the default print
Cancel = True
End Sub
Just set the print area on the tab you don't want to print to a single blank Cell, This will mean that it will print a single blank page rather than the reams of data that I presume you are trying to avoid printing.
To do this manually just select all the sheets you want to print by clicking on the first sheet tab label, then hold down the 'Shift' key and click on the last sheet tab label. (If you have a large number of sheets use the small arrow keys to navigate)
If you need to unselect a sheet in the middle hold down the CTRL key and click that sheet.
Alternately you can hold the CTRL key and click on each sheet tab label.
The sheet colour will be white (with a gradient on the none active sheet) to show they have been selected.
WARNING: When multiple sheets are selected any change on one will be repeated in the same cells on all of them.
If you do not want to use VBA or hide the sheet, you could just set print area on an empty cell of the sheet you do not want to print.
The tab will still be printed but the result will be an empty sheet.
Using an easy Macro:
Just put this lines inside a new Macro, and replace the names of the sheets yo DO want to be printed:
Sheets(Array("Name_of_Sheet_1", "Name_of_Sheet_2")).Select
Application.CommandBars.ExecuteMso ("PrintPreviewAndPrint")
Why all the crazy coding?
Just click on the first tab you want to print and make it active.
Then hold down control, and click on each additional tab you want to print, excluding the tab, or tabs you don't want to print.
Then go to "file" and then "print" like you normally would. Make sure "print active sheets" is selected, then print.
It prints only the tabs you selected.
Simple, and no need to complicate things to ridiculous levels of unnecessary silliness.

Excel VBA forcing selection of objects after run

I've researched this to death and feel like I'm the only person it's ever happened to.
I have some VBA that:
Creates a copy of 3 sheets to a new wb
In the new wb converts to values, deletes objects (shapes and controls) and all but 3 ranges
Opens an existing third file and sets the contents of three ranges in that to match the new wb
Closes the existing file (saved)
Closes the new wb (saved)
Gives a message box saying complete
At the end of all this, something weird happens with the state of the windows. The selected cell does not appear selected. If I try and click a control afterwards, it selects the object (hence users could drag them). It shouldn't and this is the big problem.
I've tried selecting a cell through code, it throws an error. I had limited success by forcing drawing mode off using Call CommandBars("Drawing").Controls("Select Objects").Execute and activating a specific sheet & selecting a cell. However, even then if I even click on a few cells afterwards, the next time I select a control it will select it as an object rather than click the thing.
I have no idea why and can't find anyone who's seen this before.
Any ideas on what I can do?
Thanks,
Basil
I didn't figure it out entirely, but I did find a fix. Hopefully it works for anyone else who finds this problem.
At the end of the code I added this:
ActiveSheet.Shapes.Range("ctrlExportPrices").Select
ActiveSheet.Range("B8").Select
So it forced a control on the sheet to be selected, and then a cell.
The next time I select the control manually, it clicks it rather than selecting the drawing object.

Making an Excel tab not print?

Is there any way in Excel to make it so that a particular tab isn't included when you print the entire workbook? As in, Sheet1, Sheet2, and Sheet3 print, but not Sheet4.
So like when someone goes to File -> Print or whatever, and hit OK, etc, everything prints out except for a particular designated tab (which I assume I have to "hide from printing" somehow).
I'm sending this workout out to a bunch of people so I'm trying to avoid forcing them to enable/run macros and all that stuff. Is there just a property of a sheet somewhere like "exclude from print"?
These answers will require the use of macros.
Hide the sheet before printing.
sheets("WorksheetToNotPrint").visible = xlSheetHidden 'or 0
or
sheets("WorksheetToNotPrint").visible = xlSheetVeryHidden 'or 2
'used if you don't want people to unhide the worksheet without knowing code
and
sheets("WorksheetToNotPrint").visible = xlSheetVisible 'or -1
when done
another option is to print the sheets you want printed (may be useful if you only want a few sheets printed:
Sub Print_Specific_Sheets()
Sheets(Array("Sheet1", "Sheet2", "Sheet4")).Select
ActiveWindow.SelectedSheets.PrintOut
Sheets("Sheet1").Select
End Sub
Actually the answer seems to be surprisingly simple.
If you hide the sheet, it will not be printed when you print the entire workbook.
You won't need a macro, just
right click on the sheet name
choose hide
If needed you or other users can unhide it as well.
If you use the BeforePrint event then you can alter what the built in print function can do. The BeforePrint macro is inside 'ThisWorkbook' under 'Workbook'.
For example you can hide sheet 4 so it doesn't print like this:
Private Sub aWorkbook_BeforePrint(Cancel As Boolean)
Sheets(4).Visible = xlSheetHidden
End Sub
The downside to this method is there is no AfterPrint method to unhide the sheets. So you need to circumvent the print dialog. There are several ways to do this but here is one very simplistic method. (Note: This might not be a good solution if you have many other printing scenarios that you need to account for)
Here I am telling exactly what sheets to print (only sheet 1 and 4). You can use names instead of numbers and you can even create a loop and look for variables to determine what sheets you want to print.
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Application.EnableEvents = False
Sheets(Array("1", "4")).PrintOut , , 1
Application.EnableEvents = True
'//prevent the default print
Cancel = True
End Sub
Just set the print area on the tab you don't want to print to a single blank Cell, This will mean that it will print a single blank page rather than the reams of data that I presume you are trying to avoid printing.
To do this manually just select all the sheets you want to print by clicking on the first sheet tab label, then hold down the 'Shift' key and click on the last sheet tab label. (If you have a large number of sheets use the small arrow keys to navigate)
If you need to unselect a sheet in the middle hold down the CTRL key and click that sheet.
Alternately you can hold the CTRL key and click on each sheet tab label.
The sheet colour will be white (with a gradient on the none active sheet) to show they have been selected.
WARNING: When multiple sheets are selected any change on one will be repeated in the same cells on all of them.
If you do not want to use VBA or hide the sheet, you could just set print area on an empty cell of the sheet you do not want to print.
The tab will still be printed but the result will be an empty sheet.
Using an easy Macro:
Just put this lines inside a new Macro, and replace the names of the sheets yo DO want to be printed:
Sheets(Array("Name_of_Sheet_1", "Name_of_Sheet_2")).Select
Application.CommandBars.ExecuteMso ("PrintPreviewAndPrint")
Why all the crazy coding?
Just click on the first tab you want to print and make it active.
Then hold down control, and click on each additional tab you want to print, excluding the tab, or tabs you don't want to print.
Then go to "file" and then "print" like you normally would. Make sure "print active sheets" is selected, then print.
It prints only the tabs you selected.
Simple, and no need to complicate things to ridiculous levels of unnecessary silliness.

Excel combobox listfillrange property pointing at a formula-based named range has issues

ActiveX combobox objects in Excel do not behave well when their ListFillRange refers to a formula-based Named Range (Defined Name).
I think I have encountered other errors and possibly even Excel crashes thanks to this, but right now all that happens is the combobox_change() event is triggered anytime ANY cell in the workbook is changed.
I am not sure if this is really a bug, or if there is a fix, or a workaround. If it is a bug, how do I report it to the Excel people?
And finally, the real meat of my question is "How do I work around this issue best?" I would like to have some formula-based named ranges, but it seems like this won't be possible.
To reproduce this bug, do the following:
Create a new workbook. On Sheet3, create a small table 3 columns across, and several rows high.
Create a named range with this formula (or an equivalent): =OFFSET(Sheet3!$A$2:$C$36,0,0,COUNTA(Sheet3!$A:$A),COUNTA(Sheet3!$4:$4)) To do this use Input>Name>Define. Name the range something like "demoRange"
Go to Sheet1 and create a combobox, (it must be on a separate sheet). (Use the Control Toolbox menu, not the Forms menu).
Click on the Design Mode button (the blue triangle with pencil), then right click on the combo box and go to Properties.
In the properties window for the combobox, change the ListFillRange property so that it points at the named range you created in step 2 ("demoRange").
You may want to change the ColumnCount property to 3, and the ColumnWidths property to "50,50,50"
Set the linkedCell property to cell "A1" by typing A1 in the linkedCell property.
Close the properties window, and double click on the combobox to define its change() event.
Put a Debug.Assert(false) or Msgbox("demo") line in the subroutine for the new combobox's change event.
Exit design mode
important - Now select an item in the combobox. The event should trigger normally the first time. (The bug will not show if you don't do this step--something must be selected in the combobox)
Edit cells anywhere in the workbook [Edit] or any other open workbook [/edit], on any sheet and any location. Each time you edit any cell, (at least for me), the onchange event for the combo box is run.
Again, is this normal, and what is the best alternative for what I am doing? This combo box gets linked to various cells, and is supposed to be a replacement for the tiny font in the data validation dropdowns excel provides by default.
My advice is to never use ListFillRange and LinkedCell. They are just trouble. Fill your listbox with List and use the Change event to write to the cell. Somewhere, maybe the Workbook_Open event, fill the listbox
Private Sub Workbook_Open()
Sheet2.ListBox1.Clear
Sheet2.ListBox1.List = Sheet1.Range("demoRange").Value
End Sub
Then in the change event in the Sheet2 module, check that something was clicked and write it to the cell
Private Sub ListBox1_Change()
If Me.ListBox1.ListIndex >= 0 Then
Sheet2.Range("A1").Value = Me.ListBox1.Value
End If
End Sub
I have a few options available that I am aware of thus far. The best I can come up with is this:
Avoid directly using formula-based named ranges. Instead, define a subroutine that will check whether the defined range "demoRange" should be changed from what its current value is. Run this subroutine on the workbook_open and sheet3_deactivate events. If needed, prompt the user to ask if it's all right to update the named range. [edit] The macro that updates "demoRange" could probably just copy from a "demoRange_FormulaBased" named range into "demoRange" which would be static. [/edit]
This solution works well because you can keep using the linkedcell property, you don't have to use VBA to populate the comboboxes, and the named range can still be used for whatever other purposes it already had. Avoid using the onchange event to run this new subroutine, since it might end up being triggered thousands of times if a user opens the Find/Replace dialog and chooses "Replace All".

Resources