What about "Application" as default object in Excel VBA? - excel

I have just written this easy macro in Excel VBA for merging a group of selected cells:
Sub Macro_Merge()
Dim Temp As String
Dim S As Variant
Temp = ""
For Each S In Selection
If Temp = "" Then
Temp = CStr(S.Value)
Else:
Temp = Temp + "," + CStr(S.Value)
End If
Next
Selection.Merge
Selection.Value = Temp
Selection.VerticalAlignment = xlTop
End Sub
This works fine, but I always see that annoying dialog box, warning me about loosing data while merging (which is exactly what I'm trying to avoid in my macro).
I can get rid of that dialog box, configuration the Application's DisplayAlerts property:
Application.DisplayAlerts = False
Selection.Merge
Selection.Value = Temp
Application.DisplayAlerts = True
This is working fine.
So, as Application is the default object, I tried to clean up my code, as follows:
DisplayAlerts = False
Selection.Merge
Selection.Value = Temp
DisplayAlerts = True
As you see, I simply omit mentioning the Application object. This is something which is allowed and I've done in the past. (If not in VBA, then Delphi, maybe?)
... but to my surprise, the dialog box appears again (although pressing F1 brings me to the official "Application.DisplayAlerts" documentation).
This leaves me with a simple question:
If a simple DisplayAlerts = ... does not equal Application.DisplayAlerts = ... anymore, what does it mean and how can I use it?
For your information, I'm working with Excel-365.

DisplayAlerts is an undeclared variable.
Certain Application properties and methods can (effectively) have the Application omitted:
ActiveCell, ActiveSheet, ActiveWorkbook, ActiveWindow, Addins, Charts, Selection, etc.
Calculate, Evaluate, Intersect, Run, Union, etc.
(but see this answer why/how this works):
A boolean property such as DisplayAlerts (EnableEvents, ScreenUpdating, etc) doesn't fall into the above category.
A golden rule in order not to fall into such a trap is the usage of Option Explicit while writing macros.

Just to add some information to the answer of #BigBen. If you write something like Workbooks or ActiveSheet in your code, VBA is not looking into the Application-object - it is looking into a (rather well hidden) object named Global.
The global object is exposing some (but not all) properties and methods of the Application-object, so ActiveSheet is referring to Application.ActiveSheet - but not because the Application has a member with this name but because the Global object defines that ActiveSheet means Application.ActiveSheet. In fact even the Application-object is accessed via the Global object.
There is hardly any information about this Global object or its concept. I found a page from Microsoft describing the Global object of MS Word, but the only explanation there is "Contains top-level properties and methods that don't need to be preceded by the Application property.". For Excel, I found this page on O'Reilly.
From time to time you get strange error messages like "Excel VBA Method 'Range' of object'_global' failed" - this is a pointer to the Global object. I would be glad to learn more about the concepts and mechanics of this object, but I am afraid that there are only very few people around that know more (except of course Mathieu Guindon AKA Mr. Rubberduck...). In daily life, we take it for granted that things like ActiveSheet simply works.

Related

Find / replace text in embedded word object code stopped working

I have used this code successfully to replace content in an embedded word object from excel. I copied the code for a new excel file but now it doesn't work. It opens the file but doesn't replace although I can see that it IS finding the right text and replacement text. I'm kind of lost as to what is happening.
Dim strFindText As Range
Dim strReplaceText As Range
Dim nSplitItem As Long
Set strFindText = ActiveWorkbook.Worksheets("Utilisation Form").Range("c11:c20")
Set strReplaceText = ActiveWorkbook.Worksheets("Utilisation Form").Range("a11:a20")
nSplitItem = strFindText.Count
Debug.Print strFindText.Item(0)
For Each sh In ThisWorkbook.Sheets("Utilisation Form").Shapes
If sh.Name <> "Object 1" Then sh.Delete
Next
Set urobj = ThisWorkbook.Sheets("Utilisation Form").OLEObjects("Object 1")
Set wordtemp = urobj.Duplicate
wordtemp.Verb Verb:=xlOpen
Set wordtemp2 = wordtemp.Object
For x = 1 To nSplitItem
With wordtemp2.Content.Find
.Forward = True
.Text = strFindText.Item(x)
.ClearFormatting
.Replacement.Text = strReplaceText.Item(x)
.Execute Replace:=wdReplaceAll
End With
Next x
End Sub
Thanks for the support
When the early-binding technology is used in the code you need to add a corresponding COM reference to be able to use data types. Otherwise, you need to declare everything from the Word object model as Object in the code and use the late-binding technology.
To use early binding on an object, you need to know what its v-table looks like. In Visual Basic, you can do this by adding a reference to a type library that describes the object, its interface (v-table), and all the functions that can be called on the object. Once that is done, you can declare an object as being a certain type, then set and use that object using the v-table. For example, if you wanted to Automate Microsoft Office Excel using early binding, you would add a reference to the Microsoft Excel X.0 Object Library from the Project|References dialog, and then declare your variable as being of the type Excel.Application. From then on, all calls made to your object variable would be early bound.
Read more about that in the Using early binding and late binding in Automation article.

Change Word table format from Excel

I have a macro in Excel that creates a Word where some Excel tables are copied, and I have this code for format changing:
Set WordTable = myDoc.Tables(i)
With WordTable
.AutoFitBehavior (wdAutoFitWindow)
.Shading.Texture = wdTextureNone
.Shading.BackgroundPatternColor = wdColorWhite
.Range.Font.TextColor = wdColorBlack
.Range.ParagraphFormat.SpaceAfter = 0
End With
Everything works properly except when the reference "Microsoft Word Object Library" is not set. In this case, the shading turns black for some reason. Is there any way to solve it, apart from set this reference?
The problem is that this macro is part of a bigger Excel program that the user installs in their own PCs, so shouldn't be able to work with VBA.
The issue is, if you use that code in Excel and "Microsoft Word Object Library" is not set, Excel does not know the Word constants wdAutoFitWindow, wdTextureNone, wdColorWhite and wdColorBlack. Instead Excel will treat them as variables and since you did not initialize them each of it has the value 0.
Make sure you use Option Explicit so you get notified if you use something that is not defined!
I recommend always to activate Option Explicit:
In the VBA editor go to Tools › Options › Require Variable Declaration.
To solve the issue, you need to either replace them with their actual value (you can find the values for the enumerations here: Enumerations (Word)) or define them as constants in Excel.
Option Explicit
Public Sub Example
Const wdAutoFitWindow As Long = 1
Const wdTextureNone As Long = 0
Const wdColorWhite As Long = 16777215
Const wdColorBlack As Long = 0
Set WordTable = myDoc.Tables(i)
With WordTable
.AutoFitBehavior wdAutoFitWindow
.Shading.Texture = wdTextureNone
.Shading.BackgroundPatternColor = wdColorWhite
.Range.Font.TextColor = wdColorBlack
.Range.ParagraphFormat.SpaceAfter = 0
End With
End Sub
You can also define them outside the scope of a procedure if you plan to use them in multiple procedures/functions. Make sure to use Option Explicit on top of every module to ensure all variables are declared properly or you will quickly run into issues again.

WorkbookOpen event sometimes not firing - event used for set global variables

I have a really basic problem with my projects and I would like to know which approach is the best. I like to use (hated) globals, only for a few the most important objects in a workbook.
I am declaring e.g. my data tables in a such way:
'#Folder("Main")
Option Exclicit
Public tblDatabase As Listobject
Public tblReport As Listobject
Sub setMyTables()
Set tblDatabase = wsDatabase.ListObjects("tDatabase")
Set tblReport = wsReport.ListObjects("tReport")
End Sub
In the past I used this macro before actions on the table, e.g.:
Function getIdFromDatabaseTable() As Variant
' set variable-object to use
setMyTables <-- I used to table-setting-sub in every
macro which requires one of my table
' get ID from table
Dim arr As Variant
arr = tblDatabase.ListColumns("ID").DataBodyRange.Value2
' assign array to function result
getIdFromDataTable = arr
End Function
But why I had to begin almost every macro with calling setMyTables() macro? So I've started to use workbook open event to set my object variables:
[code in ordinary Module]
'#Folder("Main")
Option Exclicit
Public tblDatabase As Listobject
Public tblReport As Listobject
And call setMyTables() macro in Workbook_Open() event code. And here my problem is:
[TLTR] Setting variable-objects in Workbook-Open event seems unrielable. It seems it is not firing sometimes. I am sure that no macro error would reset the project and 'clear' already set variables, because sometimes it throws error on the very first macro run. It is not working occasionally and I don't know what pattern behind it is, I send Excel workbooks to my clients, and it's hard to debug what's realy going on there.
Additional comments
I've just read that this could happen if file is not in trusted localizations, I would like get to know best approach to handle declaring the most used objects globally (if possible without modifying someones trusted folders or another local-PC settings).
I know that I can set a 'flag' bool variable such as wasWorkbookOpenEventFired, but I would have to call checking function or make ifs on almost every Sub or Function in a workbook. So I think it isn't good solution too. Thanks for hints!
You'd have more robust results if you define public functions which each return a specific table, and use those instead of global variables:
Function DatabaseTable() As ListObject
Static rv As ListObject '<< cache the table here
'if your code gets reset then this will just re-cache the table
If rv Is Nothing then Set rv = wsDatabase.ListObjects("tDatabase")
Set DatabaseTable = rv
End Function

Excel VBA Function stops execution before completion without error message

I set out to write a simple function to determine the length of a string in points. Having googled around I decided to avoid the font metrics problem by having excel do the work for me.
Here is the code.
Option Explicit
Function txtWidthPts(MyText As String) As Integer
'A cell on a working worksheet has been named "WidthTest" for easy reference & to ensure data is not overwritten.
'set WidthTest word wrapping off so that strings placed in there aren't wrapped
Application.ScreenUpdating = False
With [WidthTest]
.WrapText = False
.Value = MyText
'autofit WidthTest
.Columns.AutoFit
'get the width of the column
txtWidthPts = .Width
.ClearContents
End With
End Function
I tested the function by placing it in a cell on a working worksheet thus:
=txtWidthPts("Test123")
When I have this working I will be using it in code not as a worksheet function.
My problem is that the function does not throw an error and stops execution on the line:
.Value = MyText
I have placed the code and name into an empty workbook to ensure no interaction with other workbook contents / code.
I have searched extensively and tried various suggestions (DoEvents, Application.Update = False, etc, etc.) to no result.
I have cleared all breakpoints, closed and opened the workbook & restarted. I have tested with options set to Break on All Errors.
No result.
I suspect I am missing something obvious but it has me beat at the moment.
Any and all suggestions will be most welcome.
So, #YowE3K was right on the money. After fixing the error in the original code (Corrected code above) this runs fine from vba. I knew I was missing something obvious.
Curiosity sub-question: the function works as desired and indeed, as #YowE3K observed, it does not modify the Excel environment. However the result returned is dependent on it appearing to have modified the Excel environment. Seriously WTF. Just wanting to understand.
Thanks again YoWE3K.

MS Access VBA: Reference Excel Application Object created in separate Module

This seems like it should be an easy one but I'm stuck.
I'm running a VBA script in Access that creates a 40+ page report in Excel.
I am creating an Excel Application Object using Early Binding:
Public obj_xl As New Excel.Application
Here is an example of how I am referencing the object:
With obj_xl
.Workbooks.Add
.Visible = True
.Sheets.Add
.blahblahblah
End With
The problem is that the procedure has become too large and I need to break the code up into separate modules.
If I try to reference the Excel Application Object from a different module than it was created in, it throws an error ("Ambiguous Name").
I'm sure I could do something with Win API but that seems like it would be overkill.
Any thoughts? Thanks
this is the type of situation that can cause the error "Ambiguous Name"
Function Split(s As String)
MsgBox s
End Function
Function Split(s As String)
MsgBox s
End Function
I know the example is trivial, but what you are looking for is a function , an object and/or a form control with the same names.
If you convert your declaration to Global, you can reference it in all your modules. For example, in one module, put this at the top:
Global obj_xl As Excel.Application
Then in an another module,
Sub xx()
Set obj_xl = New Excel.Application
Debug.Print obj_xl.Name
End Sub

Resources