I want to dynamically set some data to the header (and/or footer) of a sheet.
From several forums I found numerous examples showing how to do that, and they all seem pretty easy to understand without any difficulty.
So I applied what I read to a simple try like this:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
ActiveSheet.PageSetup.CenterFooter = Range("A1")
End Sub
But it merely don't work: nothing appears in my sheet preview.
BTW the only ambiguous point in documentations was about the expression of the data to use, so I also tried replacing Range("A1") by Range("A1").Text, then by Range("A1").Value, also without success.
I suppose I'm missing some simple point...
Your code worked for me. Also tested your code with some extra lines for debugging:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Debug.Print "Running Workbook_BeforePrint"
ActiveSheet.PageSetup.CenterFooter = Range("A1")
Cancel = True
End Sub
Before printing, the footer was empty; after printing, the footer in Preview matched A1 and "Running Workbook_BeforePrint" was in the Immediate window.
It's not the beforeprint that isn't working, it's the .centerfooter. Try instead:
ActiveSheet.PageSetup.CenterFooter = CStr(Cells(1, 1))
Documentation explicitly calls for a string, but I'm not sure why it explicitly needs it as a string even if it already is a string in the cell.
As other answers already suggested, the code was fine and the problem wasn't there: in fact, the issue came from the fact that it was not invoked.
And this in turn came from a silly situation: the Design Mode was enabled!
With Design Mode disabled, all works fine...
Check yourApplication.EnableEvents is true before print sheet
Related
I've looked on some similar question of "Object required on calling sub", but None of them seems to help with my problem. I want to call a sub(for example, in sheet2) from a sub in sheet1 like
Private Sub Worksheet_Activate()
ThisWorkbook.ActiveSheet.Cells.Clear
Call sheet2.generate <== where the error comes from
End Sub
where generate is the sub name on sheet2 which don't require any parameter
but it tells me that Object required and sendback an error.
I've tried:
without using call
sheet2.generate
without sheet name
generate
call generate
and adding a useless parameter for calling( also added to generate sub)
call sheet2.generate(1)
but it's not working too, so i detele that parameter as it have no use in the code.
there is no problem on generate sub(which i test for many time)
still, all of the above do not work for me, can someone provide a vaild way to solve this?
I've found that the problem is not at calling function,but instead, stuffs INSIDE function have problem, and debug arrow goes function and says "error 1004", after fixing some variable problem of the function, the problem no longer shows.
BRUH
I'm not the most savvy VBA coder so I'd appreciate your advice! I have multiple modules where each of these modules need access to one workbook (Master.xlsm). Is it faster to 1) use a public variable to access this workbook in other modules or to 2) open it in each sub that uses it?
Option #1
I'd set the workbook to a public variable and have it assigned whenever the workbook is opened using Auto_Open.
Public wbk_MASTER As Workbook
Sub Auto_Open()
Set wbk_MASTER = Workbooks.Open("C:\Master.xlsm")
End Sub
Option #2
Alternatively, in each sub that uses Master.xlsm, I'd just pass it like:
Sub DoSomething(wbk_master as Workbook)
' do something
End Sub
That assumes that whatever sub calls this sub would look like:
Sub CallDoSomething()
Dim wbk_master as Workbook
Set wbk_master = Workbooks.Open("C:\Master.xlsm")
Call DoSomething(wbk_master)
End Sub
Note that there'd be multiple subs like DoSomething. If it makes a difference, I'd also like to have variables for important sheets in Master.xlsm and even values from specific ranges.
Personally, I think Option #1 is cleaner but which is faster?
Dirk has answered your question directly, however how about a more resilient 3rd option?
In a standard module:
Public Property Get SourceWorkbook() as Workbook
Static wkb_Master As Workbook
If wkb_Master is Nothing Then Set wkb_Master = Workbooks.Open("C:\Master.xlsm")
Set SourceWorkbook = wkb_Master
End Property
It can then be used:
Sub test()
SourceWorkbook.Sheets(1).Name
End Sub
It keeps the scope small and read only, additionally should your project be reset, will reopen the source document - neither of your approaches will do this.
To give a direct answer:
Using a global variable is "faster" in a special not noticeable way because:
What you are looking at is just a pointer to the real object.
This way there is by default no difference in speed for using globalVariable.Sheets(1).Name or subOrFunctionVariable.Sheets(1).Name.
But: Passing it to the sub/function creates a new pointer to it which takes time and uses memory.
Still: It's like blowing up a house and asking which needle makes more noise if dropped. There should never be any noticeable difference for a human being. ;)
Just looking for the use of pointers, using numerical/string/array/non-pointer-objects may create a full copy of the data (if used ByVal instead of ByRef) which may has an impact.
I am running an Excel 2010 macro that opens another workbook and removes a few code modules (a form and a BAS module). After that it re-imports them as an updated version. This is the code:
For Each x In destination_wb.VBProject.VBComponents
If LCase(x.Name) Like LCase("frmCCLogin*") Or _
LCase(x.Name) Like LCase("modCQ_test*") Then
destination_wb.VBProject.VBComponents.Remove (x)
Next
I have no problem with the import but the remove process doesn't always work as expected. For some reason the BAS (modCQ_test.bas) module is not always removed. As a result, when I re-import, a new duplicated module is created ending with a "1" (i.e. modCQ_test1.bas).
I could see that many people experienced the same problem however, none of the proposed solutions worked for me. Not sure why this is happening?
Please advise.
If you can use the exact name of the module, you can write something like:
Public Sub RemoveComponent(ByVal Book As Workbook, ByVal Name As String)
On Error Resume Next
With Book.VBProject.VBComponents
Call .Remove(.Item(Name))
End With
End Sub
If you're stuck with wildcard matching (i.e. SomeName*) you could iterate the VBComponents collection and cache the names into a collection or array or whatever and call the function above for each name matched.
Additionally, if you wish to enumerate the VBComponents collection and remove like your code sample, I recommend that you go in the reverse order.
So something like:
Public Sub RemoveComponent1(ByVal Book As Workbook, ByVal NameSearch As String)
Dim oCompS As VBComponents
Dim oComp As VBComponent
Dim i As Integer
Set oCompS = Book.VBProject.VBComponents
For i = oCompS.Count To 1 Step -1
Set oComp = oCompS(i)
If oComp.Name Like NameSearch Then Call oCompS.Remove(oComp)
Next
End Sub
Problem is resolved. This simple line of code that hides the destination workbook, fixed the duplication issue in my case:
destination_wb.Windows(1).Visible = False
After this you can remove, then add the components. No duplication will occur.
I have experienced exactly the same phenomenon and it drove me mad for weeks already.
I do check whether the Code Module had definitely been removed directly after the removal and although it had vanished from the VBE's Project View, it still exists and consequently the subsequent import creates a Code Module named xxx1.
Any of the hints given proved not to be reliable on the long run and thus are nothing but guesses. Since the phenomenon is unpredictable as mentioned you never can really tell what did the trick.
As time (some years) has passed I do now have an answer for the phenomenon and a solid and stable solution.
In order not to "cut off the branch you sit on" one will have to envoke another Workbook/VB-Project for deleting and re-importing a Component.
Even another VB-Project performing the task will have to consider that the Component is definitely removed when the code which removed it has "finished".
Conclusion: Rename, Remove, Import, all performed by a VB-Project invoked via "Run ...." will do the trick.
I have a spreadsheet, with the following two subroutines in it (there's a lot more to them, but I've stripped out all the code not directly relevant to the question):
Sub HF_Reset()
Feats_Reset
End Sub
Sub Feats_Reset()
Range("TblAllFeatsSelected").Value = CVErr(xlErrNA)
Range("Test").Value = "Success"
Range("Test2").Value = 1
End Sub
Test is a single cell, Test2 is a two-cell range, TblAllFeatsSelected is a large range.
If I call Feats_Reset, it executes absolutely fine, does what it's intended to do. If I call HF_Reset, then Testgets "Success" put into it, and Test2 is filled with 1s, but TblAllFeatsSelected doesn't change. I have absolutely no clue what's going on - any ideas?
For debugging purposes, I've also tried setting Range("TblAllFeatsSelected").Value = 1 and Range("TblAllFeatsSelected").Value = 0, and again it works fine when calling Feats_Reset but not when calling HF_Reset.
EDIT: I've played some more, and traced the problem to another subroutine called in Feats_Reset. I suspect I'm not going to be able to provide enough information here to get a useful answer - it's a complicated sheet, and there's a lot of interactions that could be the problem. Bother.
EDIT2: Found the problem. The subroutine was setting TblAllFeatsSelected to the value of another range, which when calling from HF_Reset needed to have an Application.Calculate or it would justset it back to what it used to be.
Is there any way I can delete this question as not useful? It's such a specific thing, I doubt it could help anyone else.
Problem Exists Between Keyboard and Chair. I was missing an Application.Calculate in a completely different part of the code.
I have a user-defined function that takes a parameter which has an associated builtin enum:
Public Function bgrcolor_cells(rng As Range, xlcl As Long) As Integer
I want to write a formula like this in a cell
=bgrcolor_cells($A2:$B2,vbRed)
instead of
=bgrcolor_cells($A2:$B2,255)
Is that possible? How?
PS: I have found How can I use enum identifiers in Excel UDF, but it refers to user-defined Enums. I do not know if the fact that I want to use a builtin Enum makes a difference.
EDIT 1
Perhaps some automated reading of the code where the builtin Enum is defined may help in defining either the Class by Jean-François Corbett or named ranges, and then one would avoid typing from scratch. I have seen such automated parsing, possible pointers are:
http://www.cpearson.com/excel/EnumNameList.aspx
http://www.excelforum.com/excel-programming-vba-macros/356892-programmatic-generation-of-enum-to-string-functions.html
EDIT 2 (as per this)
A comment has been posted stating that "there is no difference for built-in and user-defined enums", and that this is a dupe. I think it is not, and that if the quoted comment is correct, then it may be part of an answer (perhaps worth posting as such) for the present specific and different question.
The question you link to already covers this topic and the accepted answer should work just fine. There is no particular shortcut for VBA built-in enums.
Otherwise you can try something like the following.
For entertainment purposes only
Before I get lynched for this, I'd like to say that I did this just for fun as a proof-of-principle and would probably never use this myself!
Create a class called ColorEnums:
Option Explicit
Public vbRed As Long
Public vbGreen As Long
'etc.
Private Sub Class_Initialize()
vbRed = VBA.vbRed
vbGreen = VBA.vbGreen
'etc.
End Sub
Make a user-defined function like this:
Function GetValueOfColorEnumByName(colorEnumName As String)
GetValueOfColorEnumByName = CallByName(New ColorEnums, colorEnumName, VbGet)
End Function
Where CallByName allows us (and this is very ugly) to evaluate the value of a member of a class from its name in a string.
Example usage:
Adapt to your own requirements, at your own risk.