Frequent content manipulation on the screen, particularly in large documents, can slow a macro and cause unpleasant screen flickering while it runs. Today, we improve two previous macros to toggle several screen settings. The finish line is the same, but with this version, we safeguard against subsequent calls in other macros prematurely restoring the settings.
Thanks for your interest
This content is part of a paid plan.
Toggle Screen Updates
Of course, removing inefficiencies is the most important way to upgrade a slow macro, but we can also tweak a few Word settings to improve the speed and presentation of our macros. This version also allows us to restore some settings to their original settings rather than always turning them off and then back on.
We also encounter some limitations of VBA in the process. There is some overlap with this article and the free version.
What’s the plan?
Turning off screen updates during macro execution will prevent Word from repeatedly updating the display until our macro is finished. This also ensures our macro runs cleaner without displaying all the unsightly text manipulations in progress. Users just see the final result.
We can further limit some calculations Word does while the macro is running since Word is constantly checking and updating the document under the hood while you work. In real-time use, this is inconsequential, but it can affect macros since the changes happen much faster. However, with just flip a few simple switches, Word will wait until we’re done before resuming regular document analysis or updates.
What do we change?
How do we make the advertised changes?
Screen updates
The screen updates setting is part of the top-level Application object. Assuming the macros are written reasonably well, turning it off will improve the speed of many macros.
As the name implies, we set it to True or False value depending on whether we want to enable or disable screen updates, respectively.
Document pagination
If a macro makes any sizeable changes to documents, particularly large ones, it may also help to temporarily disable document pagination.
Of course, False disables Word recalculating the page layout of the document in real time as the macro runs. At the end of the macro, we turn it back on, and Word can recalculate the pagination just once.
Spelling and grammar
We can also turn off real-time grammar and spell checking since Word is constantly checking the document as you write.
This controls whether Word checks spelling in real-time while you type.
This setting controls whether Word also checks grammar while it checks the spelling in the document.
Of course, if you always work with spell checking turned off, you can omit these settings.
In practice, I haven’t had much problem with grammar and spell checking hindering my macros, but it makes sense to toggle them off while we’re running a complicated macro.
Problems with previous solution
Our earlier, simpler approach used only subroutines (called procedural programming) to toggle the screen settings. Most macros in VBA work well with this approach since they are usually handling isolated tasks.
More specifically, the previous subroutines enabled and disabled several document settings during macro execution. They did the job, but any macro or function could still toggle the settings back on at any time. Admittedly in VBA, we can’t stop a user from flipping these switches manually anywhere since the individual settings are always available, but we can tighten our own controls a little while also making the process easier to manage.
Also as you work on more complicated macros, are you always going to remember which macros also toggle the screen settings? It would be nice if our macros took care of that for us.
If we try to stay with the procedural approach, we would probably need to resort to using global variables to store information since we would have multiple functions accessing the same variables (“static” variables would work for a lone function). In general, using global variables is probably a poor(er) solution because they can be changed anywhere at any time. It’s like having a toybox where the neighborhood kids can enter your room and ravage your toys whenever they wish. Imagine owning a toy, but the pieces scattered across the room every time you want to play with it. That doesn't sound like fun to me.
Class Version
Most people don’t tinker with a car’s engine when stopped at a red light. The manufacturer provides a gas pedal and a brake. They probably also include air conditioning and other assorted controls. Maybe some blinking lights and such.
The driver just tells the car what to do, turns a knob, or flips a switch here or there, and the car handles the details. Most people don’t want to work on the engine or anything of the sort even after they get home. The car is just an important tool to get other work done.
What are classes?
That’s kind of like a class. They are the software equivalents of real-life objects, and indeed they’re called “objects” in programming also.
We give the user some defined data (properties) and actions (methods). The user writes a macro using these higher-level commands, and the class handles most of the details. The class provides some structure, so she can solve bigger problems elsewhere.
Before we get started, this lesson isn’t meant to be a tutorial on classes in VBA. That’s a much bigger topic than our implementation merits.
Why use a class for this problem?
I initially resisted creating a class here, but I eventually realized that we have several pieces of data in the form of screen settings and several actions we can take when using that data.
Connecting data and actions on that data is the essence of “object-oriented programming”. We put the two together to form a cohesive, logical object hopefully making it easier to understand and use, and classes are how we implement these objects.
Creating a class to solve this particular problem is a little overkill, but it simultaneously gives us more control over the process while also making it easier to use.
What’s the new problem to solve?
What happens when multiple functions try to toggle the screen settings in succession?
In more complicated problems, we might have macros calling other macros. We could easily forget which macros toggle the screen updates, so we would like the class to control the process and ignore any attempts to restore the screen settings when inside any later functions that are called from the main macro.
While we’re at it, it would be nice if the class allows us to restore some settings to their starting values rather than always on or off.
What do we need to do?
We have several specific things we need the class to do.
- Initialize any saved variables (whatever they are) but just on first use in most cases
- Disable the screen settings, but ignore any subsequent attempts to disable them while they are already disabled. Also, store any unique ID given by the user for later validation.
- Enable the screen settings but only if currently disabled and only they’re being changed by the original macro that disabled them when using a unique ID
- Check the current disabled status (usually obvious based on general use cases, but we might need it sometime)
Screen settings class
The following class is probably the cleanest overall way VBA allows us to implement screen setting toggles during macro execution.
Create a new class module
Create a new class module in the VBA editor. From the menu, use Insert → Class Module.
In the Properties information panel (use the menu View → Properties Windows if it's not visible), give the class an appropriate name like TScreenSettings. I include the preceding "T" just to ensure I remember it's a data type not just a variable name, but it's not required.
Then insert the following code into the empty module (full text is below).
Screen settings data
Private variables and methods prevent anyone from manually changing or using them from anywhere outside the class. In this simple class, there isn’t a need for anyone to directly access them anyhow. To declare a class variable as private, we literally just insert Private before the variable name.
Of course, we also need to give a variable type (if we want to be specific). Here we use Boolean variables since we’re storing True-False screen settings.
InProgress records whether the disabled state is in progress. It is True if screen settings are disabled and False if enabled. Of course, "enabled" screen settings is the regular working state in Word unless the user has turned off spelling and grammar checking.
The sMacroID variable allows us to identify which macro or function initially disables the screen settings. If provided, the macro ID is stored during a successful Disable command (see below). A successful call to a subsequent Restore method below would require the correct macro ID. This prevents unintended macros from restoring the screen settings too early before the main macro finishes.
The ID usage does not prevent screen settings changes from other variable instances since they cannot talk to each other easily (see Gotchas below), but general class usage only requires one global ScreenSettings variable.
The class allows any macro to toggle the screen settings on or off if sMacroID is an empty string “”.
Class subroutines
The following two private subroutines are only for the class to create or destroy itself.
Class_Initialize is run automatically by VBA when the object is first created.
We initialize InProgress to False since nothing is happening yet, and we store the initial spelling and grammar settings using the Initialize method below.
Class_Terminate is run automatically by VBA when the object is deleted. In case the user forgot, we make sure to Restore the screen settings just before the object is terminated. There is a gotcha here though when using global variables (see below).
Public methods
Public means anyone can use these methods, and public is the default in VBA if Public or Private are both omitted. These are the functions that the class creator expects a typical person to use.
Initialize settings
We need to store the current values for selected settings.
For now, we only store the spell check and grammar settings since we’ll always toggle the ScreenUpdates or Options Pagination on or off for restore or disable them, respectively.
This initialization is a separate subroutine in case we want to do so separately sometime, but it probably won’t be needed in most cases since the class initializes the screen settings and other variables automatically.
Restore screen settings
The Restore subroutine checks to make sure we’re InProgress before restoring the screen settings.
If provided, the macro ID must match the stored value before anything is changed. This helps us control which macro can turn the settings back on. The ID is stored in the Disable method below, if desired.
Restoring the settings consists of turning ScreenUpdating and Pagination back on and resetting the stored values of grammar and spell checking.
InProgress is always False when leaving the Restore method.
Disable screen settings
The Disable subroutine checks to make sure we’re not already InProgress before disabling any screen settings.
Disabling the settings consists of turning ScreenUpdating, Pagination, grammar, and spell checking all off.
InProgress is always True when leaving the Disable method meaning we are inside a macro (possibly also calling other macros or functions) making document changes that would be otherwise be shown on the screen.
Macro ID stored
If a macro ID is provided, it is stored for later validation during a Restore attempt. The intention is only the starting or main macro can restore the screen settings later.
Macro ID issues
This approach requires the user to provide a unique ID in each macro or function where Disable and Restore are used.
Ideally, we would obtain a unique identifier of some sort from the main macro and use that as a better ID without requiring user input. The user wouldn’t even have to know the ID value. Unfortunately, VBA gives us no way to identify either the function address nor the function name within a macro (without third party tools which is very much overkill for this problem). As a result, we’re stuck with requesting a unique macro ID string from the user.
This is not as “safe” in the sense that we allow more chances for mistakes to creep into the process, but VBA doesn’t give us much choice.
Get status
The GetStatus function is mostly in case a macro wants to check whether the screen settings are disabled. It is included for completeness, but it should not be needed in general.
Does macro ID match?
A macro ID controls which macro is allowed to restore the screen settings. With this in mind, it is convenient to provide a separate method to validate whether an ID is correct or not.
The method is public, meaning anyone could check a valid macro ID string, but in practice, it probably wouldn’t be necessary.
It returns a True-False value based two criteria:
Is the macro ID defined?
Not defined is when sMacroID is an empty string "", and it results in a True value for this method.
Does the given macro ID match the stored ID?
We literally just compare the two strings which must match exactly.
The macro ID is initially stored when the Disable command is first used, and a subsequent Restore can reverse the change if the command has a matching ID.
No data properties
A typical class would probably give a user access to the data using “properties” (which are basically functions that control access to the internal class data), but this class and its use cases are simple and general enough that it’s cleaner to just restrict any data access from outside the class.
Gotchas
These gotchas cannot necessarily be solved, but we should still be aware of them.
Not turning screen updates back on
What happens if a macro doesn’t turn screen updates back on perhaps because it crashes before the macro ends. In my experience, Word won’t let screen updates nor the pagination option stay off after the macro finishes, so we’re probably safe. However, I have seen rare cases where it seemed like screen updates did not get toggled back on correctly when a macro crashed. Fortunately, in a worst-case scenario, you could just save the document and restart Word.
However, it’s bad manners and risky to take chances with sloppy assumptions, so you should definitely restore the settings by the end of the main macro.
The class approach attempts to catch this in the termination method if the user forgets, but since the screen setting is declared as a global variable, it doesn’t actually get deleted until Word quits. This means the user is still responsible for restoring the screen settings.
If you want to be safer, see the extended content in the previous article where we introduce using some simple error handling to ensure screen settings are restored properly even if there is an error in the main macro.
Navigation pane is still updated
Unfortunately, the navigation pane is still updated during macro execution even when screen updates are turned off. There doesn’t seem to be much we can do about that as regular users, so you’ll probably still see the navigation pane text jiggle if you have it visible.
Why not declare a different ScreenSettings object in each macro?
If we declare a new ScreenSettings object in each macro, then we could guarantee the settings are restored when the macro ends. The terminate method would automatically restore the screen settings before deleting the variable.
There are at least two problems with this approach:
Multiple variables would not communicate
If we use multiple ScreenSettings variables, they do not communicate well with each other (see below). In the end, we would have to regularly check and probably locally store what the current screen settings state was upon entering the macro before allowing it to restore any settings at the end. Unfortunately, this defeats the whole point of using the class to solve the original problem. The class is supposed to handle the details for us.
If you’re familiar with other programming languages, you might start to interject:
“But static variables—”
Hold on. They don’t work the same way here …
Cannot access VBA static variables in multiple functions
Generally, something like this would be handled with a “static” class variable which exists once for the class as opposed to separately for each variable instance of the class. We would then use that static variable to store which function disabled the screen settings and whether they are currently disabled. This unique information would be visible to all instances of the class.
And you hear the but coming ...
Unfortunately, VBA doesn’t implement static class variables in the same way as other programming languages. That is, static variables exist in VBA classes but at a procedure level meaning the variable stays local to the procedure.
Why? I don’t know.
But if we want to access InProgress and sMacroID from different class methods to validate the status or ID before taking some action, we can’t use VBAs version of static variables.
Ughhh.
Thus, the best way to track whether it’s “in progress” or which macro disabled the screen settings across multiple of them is by using a single global screen settings variable.
This is another VBA shortcoming. While VBA is nice overall, making macros far more approachable to write for regular users, it doesn’t quite match the capabilities of some other languages.
Verdict
In the end, none of the various possible solutions are ideal in VBA, but I this seems to be the best of the available approaches (as far as I can tell) to control screen settings without getting into potential trouble with overlapping calls to enable or disable them.
Revised Screen Settings Class with ID
Putting it all together, the screen settings class includes:
How to use the screen settings class
How do we actually use this class to toggle screen settings?
Declare global variable
At the top of the main module in the VBA editor, declare the global screen settings variable like so:
This will automatically run the initialization function (called a “constructor” in other languages) and set up to allow a macro in the project to toggle the screen settings.
But you said—
Yes, this uses a global variable that I said was generally a bad thing, but the class controls access to the specific settings in a way that the user only needs to enable or disable them. This is safer and cleaner than just having the “raw” settings visible to everything in your project.
Only one screen settings variable needed
There should be no need to create another screen setting variable since the global one does everything we need (but see last gotcha above).
Using screen settings class
Once the variable has been declared at the top of your main module, inside any macro, function, or subroutine where you want to toggle the settings, just use:
Before you exit the main macro, add the command:
The UniqueID can be omitted, but it will allow any macro or function to Restore the settings. The macro ID allows us to restrict a proper restoration of the screen settings only at the end of the original macro used to disable the settings. If you happen to enable and disable the screen settings multiple times across other macros, the class will only honor the last Restore command inside the starting macro.