Dual Monitors And UserForms
A UserForm object has a property named StartUpPosition. By default, this property is set to 1 (CenterOwner), which means that it should appear in the center of Excel's window.
This works fine, unless you have a dual-monitor system. In such a case, the UserForm is not centered in the Excel window. On my system, UserForms always display on the left side of the secondary monitor, even if Excel is maximized on the primary monitor.
To force an Excel UserForm to be centered in Excel's window, use this code to display the UserForm:
With UserForm1 .StartUpPosition = 0 .Left = Application.Left + (0.5 * Application.Width) - (0.5 * .Width) .Top = Application.Top + (0.5 * Application.Height) - (0.5 * .Height) .Show End With
I was hoping this would be fixed in Excel 2010, but nothing has changed. I'm
sure most of the Microsoft programmers use at least two monitors, so they are
obviously aware of the problem.
Pasting An Image To A UserForm Control
VBA programmers are familiar with Excel's UserForms. The UserForm object, as well as many of the ActiveX controls that are placed on a UserForm, can display an image -- the object's Picture property.
How to set the Picture property
If you believe Excel's online help, you'll think that there are two ways to set the Picture property for an object:
- At design time: Browse for an image file, by clicking the "..." button in the Properties box
- At run-time: Use VBA's LoadPicture function to retrieve an image file.
But there's another way. And it doesn't even require that the image is stored in a file. Here's the trick:
- Locate the image that you want to use, and copy it to the Clipboard.
- Activate the VB Editor and select the object that will contain the image (i.e., a UserForm, or a control that has a Picture property).
- Make sure the Properties window is displayed (press F4 it it's not).
- Click the Picture item. By default, this displays "(None)" as in the figure above.
- Press Ctrl+V to paste the image on the Clipboard to the control.
Importantly, the picture that you paste to a control need not be stored in a file. Any picture that can be copied to the Clipboard can be pasted to an object's Picture property. And this includes charts stored in a worksheet. The trick here is to copy the chart as a picture: Click the chart, press Shift, and choose Copy Picture from Excel's Edit menu. Once copied, you can paste the picture of the chart to your UserForm control.
When pasting a chart, it's important to understand that the picture is not linked to the original chart. So if the chart changes, the picture will not change.
A companion file is available: Click here to download
This tip describes a useful technique that lets you display a series of help topics in an Excel workbook.
How it works
The technique described here uses a UserForm, and the UserForm has the following controls (see the Figure below):
- A DropDown control (which displays the current help topic title)
- A Label control that displays the help topic text
- A Frame control. The label is inside of the frame to allow scrolling if all of the text isn't visible.
- Three buttons: Previous, Next, and Exit
The help topics are contained on a worksheet, and the VBA code simply reads the text in the worksheet and transfers it to the UserForm. The user can select a help topic from the DropDown, or view the topics sequentially using the Previous and Next buttons.
You can easily adapt this technique to your own project:
- Export the UserForm and Import it to your project
- Set up the worksheet with the help topics and help text
- Create a simple macro to display the UserForm
Pros and Cons
This technique is very easy to set up, and requires no additional software or files. The VBA code is contained in the UserForm, so no additional programming is necessary. The primary disadvantage is that you cannot format the text that is displayed.
General Userform Tips
One of the most useful features in Excel is the ability to create custom dialog boxes (UserForms). Before embarking on this task, ask yourself if a custom dialog box is really necessary. In some cases you may be able to use one of the following options to get user input:
- VBA's MsgBox function
- VBA's InputBox function (always returns a string)
- Excel's InputBox function (you can specify the type of data returned)
- Excel's GetOpenFilename method
- Excel's GetSaveAsFilename method
- Excel's FileDialog object (Excel 2002 only)
And, don't forget that your VBA code can display most of Excel's built-in dialog boxes. For example, the following subroutine displays the Move or Copy dialog (used to move or copy a sheet):
Sub ShowMoveCopy() Application.Dialogs(xlDialogWorkbookMove).Show End Sub
When designing your userForm, a good rule of thumb is to make the dialog boxes look and work like those that are built into Excel. If you're a beginner, you may find it instructive to try to recreate a particular Excel dialog box. You may find a few situations in which a built-in dialog box can't be recreated exactly.
Before you unleash your UserForm, give it one final check using the following checklist.
- Are the controls aligned with each other?
- Are similar controls the same size?
- Are controls evenly spaced?
- Is the dialog box too overwhelming? If so, you may want to use a series of dialogs (like a Wizard), or use a MultiPage control.
- Can every control be accessed with an accelerator key?
- Are any of the accelerator keys duplicated?
- Are the controls grouped logically (by function)?
- Is the tab order set correctly? The user should be able to tab through the dialog box and access the controls sequentially.
- If the UserForm will be stored in an add-in, did you test it thoroughly after creating the add-in? It's important to remember that an add-in will never be the active workbook.
- Will your VBA code take appropriate action if the UserForm is canceled, or if the user presses Esc?
- Are there any misspellings in the text? Excel's spelling checker doesn't work with UserForms.
- Does the UserForm have an appropriate caption?
- If applicable, will your UserForm fit on the screen in lower screen resolutions?
- If your UserForm will be used in multiple versions of Excel, have you tested the application on all versions?
- If you use help, is the help topic correct? And does it explain all the controls?
Displaying A Progress Indicator
A companion file is available: Click here to downloadA common question among Excel developers is, How can I use a custom dialog box to display the progress of a lengthy macro?
This document describes how to create an attractive progress indicator with minimal effort.
Creating the UserForm
Follow the steps below to create the progress indicator UserForm.
- Insert a new UserForm and change its Caption to Progress.
- Add a Frame control and name it FrameProgress.
- Add a Label control inside of the Frame and name it LabelProgress. Remove the Label's caption, and make its background color red.
- Add another label (option) to describe what's going on.
- Adjust the form and controls so they look like this:
Creating the Event-handler subroutines
The trick here involves running a subroutine automatically when the dialog box is displayed. Since the Initialize event occurs before the dialog box is actually show, you must use the Activate event. Insert the following subroutine in the Code window for the UserForm. This subroutine simply calls the Main subroutine (stored in a VBA module) when the UserForm is displayed.
Private Sub UserForm_activate() Call Main End Sub
The Main subroutine is listed below. This demo routine simply inserts random numbers into the active worksheet. As it does so, it changes the width of the Label control and displays the percent completed in the Frame's caption. You will, of course, substitute your own subroutine. And you'll need to figure out how to determine the progress complete.
Sub Main() ' Inserts random numbers on the active worksheet Dim Counter As Integer Dim RowMax As Integer, ColMax As Integer Dim r As Integer, c As Integer Dim PctDone As Single If TypeName(ActiveSheet) <> "Worksheet" Then Exit Sub Cells.Clear Application.ScreenUpdating = False Counter = 1 RowMax = 100 ColMax = 25 For r = 1 To RowMax For c = 1 To ColMax Cells(r, c) = Int(Rnd * 1000) Counter = Counter + 1 Next c PctDone = Counter / (RowMax * ColMax) With UserForm1 .FrameProgress.Caption = Format(PctDone, "0%") .LabelProgress.Width = PctDone * (.FrameProgress.Width - 10) End With ' The DoEvents statement is responsible for the form updating DoEvents Next r Unload UserForm1 End Sub
Creating the start-up subroutine
All that's missing is a subroutine to display the dialog box. Enter the following subroutine in a VBA module.
Sub ShowDialog() UserForm1.LabelProgress.Width = 0 UserForm1.Show End Sub
How it works
When you execute the ShowDialog subroutine, the Label object's width is set to 0. Then the Show method of the UserForm1 object displays the dialog box. When the dialog box is displayed, its Activate event is triggered, which executes the Main subroutine. The Main subroutine periodically updates the width of the Label. Notice the DoEvents statement. Without this statement, changes to the Label are not visible. Before the subroutine ends, the last statement unloads the UserForm object.
This is definitely a slick technique, but you should be aware that it may
slow down your macro a bit due to the extra overhead of updating the progress
indicator. In many cases, however, it is not even noticeable.
Importing And Exporting Userforms
If you've worked with UserForms, you've undoubtedly noticed that inserting a new UserForm results in an empty dialog box. You might prefer to add a UserForm that already has some controls on it (for example, an OK button and a Cancel button).
In this document I describe a simple technique that lets you create a new "default" UserForm that contains these two buttons. The procedure can be adapted for other controls.
Creating the default UserForm
Follow these steps to create a UserForm.
- Start with a blank workbook.
- Press Alt+F11 to activate the Visual Basic Editor (VBE)
- In the Project window, select the blank workbook
- Select the Insert UserForm command. An empty UserForm is added to the project.
- Use the Toolbox and add a CommandButton to the form.
- Change the following properties for the CommandButton:
- Use the Toolbox and add a second CommandButton to the form.
- Change the following properties for this CommandButton:
- Double-click the Cancel button to activate the Code window for the UserForm.
- Modify the CancelButton_Click subroutine as follows:
Private Sub Cancel_Button_Click() Unload Me End Sub
Exporting the UserForm
The next step is to export this UserForm.
- Make sure the UserForm is selected in the Project window.
- Select the File Export File command.
- Enter a name for the exported UserForm. Use a descriptive name, like NewDefaultForm
The preceding steps saved the UserForm and code to a file.
Importing the UserForm
When you need to add a new UserForm to a project, you can save time by importing the file you saved.
- Make sure your project is selected in the Project window.
- Select the File Import File command.
- Locate the file you exported in the previous section.
- Use the Properties window to give the new form a descriptive name. This step is necessary if you later decide to import the file again to add another UserForm.
Handle Multiple Userform Buttons With One Subroutine
A companion file is available: Click here to download
When you create a UserForm, every control on the form must have its own event handler procedure. For example, if a UserForm has 12 CommandButtons, you need 12 procedures to handle the click events for those buttons.
This tip describes a way around this limitation by using a Class Module to define a new class.
- Create your UserForm as usual, and add several CommandButtons.
- Insert a Class Module and enter the following code. You will need to customize the ButtonGroup_Click subroutine.
Public WithEvents ButtonGroup As CommandButton Private Sub ButtonGroup_Click() MsgBox "Hello from " & ButtonGroup.Name End Sub
- Insert a normal module and enter the following code. In this code, I exclude a button named OKButton from the "button group." Therefore, clicking the OK Button does not execute the ButtonGroup_Click subroutine.
Dim Buttons() As New Class1 Sub ShowDialog() Dim ButtonCount As Integer Dim ctl As Control ' Create the Button objects ButtonCount = 0 For Each ctl In UserForm1.Controls If TypeName(ctl) = "CommandButton" Then If ctl.Name <> "OKButton" Then 'Skip the OKButton ButtonCount = ButtonCount + 1 ReDim Preserve Buttons(1 To ButtonCount) Set Buttons(ButtonCount).ButtonGroup = ctl End If End If Next ctl UserForm1.Show End Sub
- Execute the ShowDialog subroutine to display the UserForm. Clicking any of the CommandButtons (except the OKButton) executes the ButtonGroup_Click subroutine.
Note: To use this technique with other types of controls, change the WithEvents statement. For example:
Public WithEvents LabelGroup As MSForms.Label
Notice that you must qualify the Label object because Excel also has an object named Label. Also, you will need to make appropriate changes throughout the ShowDialog procedure.
Filling A Listbox With Unique Items
A companion file is available: Click here to downloadWhen you display a list of items in a ListBox, you generally don't want to show duplicate items. This tip describes a clever way of filling a UserForm with unique items from a list. This technique is adapted from a tip by J.G. Hussey, published in Visual Basic Programmer's Journal.
How it works
This tip uses a Collection object, and relies on the fact that VBA generates an error if you attempt to add an item to a collection when the item already exists in the collection. The trick is to build the collection by adding all items to it, and ignore the errors that may occur. The result is a collection of unduplicated items.
I created an example to demonstrate the technique. The items (105 of them) are stored in Column A of a worksheet. Many of these items are duplicated. The RemoveDuplicates subroutine, listed below, builds a collection that consists of the unique items in the list. It then transfers the items to a ListBox on a UserForm.
Following is the VBA listing.
Option Explicit ' This example is based on a tip by J.G. Hussey, ' published in "Visual Basic Programmer's Journal" Sub RemoveDuplicates() Dim AllCells As Range, Cell As Range Dim NoDupes As New Collection Dim i As Integer, j As Integer Dim Swap1, Swap2, Item ' The items are in A1:A105 Set AllCells = Range("A1:A105") ' The next statement ignores the error caused ' by attempting to add a duplicate key to the collection. ' The duplicate is not added - which is just what we want! On Error Resume Next For Each Cell In AllCells NoDupes.Add Cell.Value, CStr(Cell.Value) ' Note: the 2nd argument (key) for the Add method must be a string Next Cell ' Resume normal error handling On Error GoTo 0 ' Sort the collection (optional) For i = 1 To NoDupes.Count - 1 For j = i + 1 To NoDupes.Count If NoDupes(i) > NoDupes(j) Then Swap1 = NoDupes(i) Swap2 = NoDupes(j) NoDupes.Add Swap1, before:=j NoDupes.Add Swap2, before:=i NoDupes.Remove i + 1 NoDupes.Remove j + 1 End If Next j Next i ' Add the sorted, non-duplicated items to a ListBox For Each Item In NoDupes UserForm1.ListBox1.AddItem Item Next Item ' Show the UserForm UserForm1.Show End Sub
Creating A Color Picker Dialog Box
A companion file is available: Click here to downloadThis tip demonstrates a technique to display a dialog box that allows the user to select a color.
How it works
The example file contains the following:
- A UserForm (UserForm1) that contains a dialog box with 56 buttons.
- A Class Module (Class1) that defines a ColorButton Class.
- A VBA module (Module1) that contains a function (GetAColor).
- Two example subroutines that demonstrate the GetAColor function.
The GetAColor function sets up the dialog box and display it (see the figure below). The function returns the color value of the selected button. If the user clicks Cancel, the GetAColor function returns False. As the user moves the mouse pointer over the color buttons, the Color Sample image displays the color.
Using the function
To use the GetAColor function in your own workbooks, export UserForm1,
Module1, and Class1, and then import them into your workbook. Write your own
subroutine that calls the GetAColor function.
Displaying A Chart In A Userform
A companion file is available: Click here to downloadOne of my favorite Excel tricks is to display a chart in a UserForm. You might think that this would be easy: Just add a linked picture of the chart to the UserForm. Unfortunately, that's not possible.
This tip describes a workaround that involves saving the chart as a GIF file, and then displaying the GIF in an Image control. This ensures that the UserForm always displays the current version of the chart.
You might notice a slight delay as the chart is saved and then retrieved. On a fast system, however, this delay is barely noticeable.
How it works
To set this up:
- Create your chart or charts as usual.
- Insert a UserForm and then add an Image control.
- Write VBA code to save the chart as a GIF file, and then set the Image control's Picture property to the GIF file. You need to use the LoadPicture function to do this.
- Add other bells and whistles as desired. For example, the demo file displays multiple charts, and the user can cycle through them.
Saving a chart as a GIF file
The code below demonstrates how to create a GIF file (named temp.gif) from a chart (the first chart object on Sheet1).
Set CurrentChart = Sheets("Sheet1").ChartObjects(1).Chart Fname = ThisWorkbook.Path & "\temp.gif" CurrentChart.Export FileName:=Fname, FilterName:="GIF"
Changing the Image control's Picture Property
If the Image control on the UserForm is named Image1, the statement below loads the image (represented by the Fname variable) into the Image control.
Image1.Picture = LoadPicture(Fname)
Search for Tips
Browse Tips by Category
Needs tips? Here are two books, with nothing but tips:
Contains more than 200 useful tips and tricks for Excel 2007 | Other Excel 2007 books | Amazon link: John Walkenbach's Favorite Excel 2007 Tips & Tricks