I searched the internet a bit before deciding that neither Excel or the forums have a way to track Activecell movement in a Selection. So, even though I don’t have the most pressing need for such an event, I spent several quality hours coding one.
The Issue
Here’s what I’m talking about. You can capture cell activation using Selection_Change code:
Me.Range("A2") = ActiveCell.Address
Me.Range("B2") = ActiveCell.Value
End Sub
But this doesn’t capture cell activation within a selection, the kind that happens when you select an area and then tab through it:
I noticed this while creating a little ActiveCell Viewer form. My solution is to capture TAB key presses using Application.OnKey. This is a little clunky in a couple of ways, but it seems to work.
My Solution
So my userform uses OnKey to capture the Tab press when a Selection has more than one cell. Now I know when the active cell is changing within a selection, so that’s good. The hard part is that, since I’ve killed the normal Tab behavior, I have to recreate it in VBA with code that moves the ActiveCell one cell forward. Before I can do that I need to define the active cell’s location in terms of the selection.
Basically, the code cycles through each cell in each Area of the Selection. The Areas cycle through in the order they were selected. Tab cycles through each cell of an Area from top to bottom, right to left no matter how you selected the Area.
A Little More About Areas
I do believe that areas are always rectangular. If you make a selection with the control key down, each down-drag-up of the mouse represents an area. I’ve delved into this area (hah) before in the SelectTracker post. As indicated there, if you hold down Ctrl and click a cell five times you get a Selection with five Areas. Interestingly, Excel 2016 gives the unsuspecting user a bit of a hint that this is happening by making the cell a little darker with each click:
Back to the Coding Process
At first, as so often happens, the VBA seemed fairly simple:
- Find the ActiveCell’s position within the overall Selection by looping through its Areas and through each Area’s cells.
- Figure out what the next (or previous) cell would be, accounting for moving from one Area to the next and hopping to the beginning of the next Area or looping back to the beginning of the whole Selection.
Then I started thinking about Selections with multiple Areas that intersect the ActiveCell. To deal with this I did two things needed to make the tabbing flow smoothly through overlapping Areas.:
- Created a global variable to track which of these areas was selected by the last Tab press.
- Created a short routine to figure out the “most recent” Area that intersects the ActiveCell
The Code
Here’s the code that identifies the Activecell’s position within the Selection’s Areas and Cells:
'This sub is called when the Tab key is pressed.
'The point is to be able to capture Tab key presses
'that occur within a Selection, thereby capturing movement of the Activecell.
Dim SelectionCellIndex As Long
Dim SelectionCell As Excel.Range
Dim SelectionArea As Excel.Range
For Each SelectionArea In Selection.Areas
SelectionCellIndex = 0
'Mod lets us cycle to the Area after we reach the last one
SelectionAreaIndex = (SelectionAreaIndex Mod Selection.Areas.Count) + 1
For Each SelectionCell In Selection.Areas(SelectionAreaIndex).Cells
SelectionCellIndex = SelectionCellIndex + 1
If SelectionCell.Address = ActiveCell.Address Then
GoTo SelectNext
End If
Next SelectionCell
Next SelectionArea
SelectNext:
'Cycle to the next cell. If we're in the last one, we'll cycle to first.
SelectionCellIndex = (SelectionCellIndex Mod Selection.Areas(SelectionAreaIndex).Cells.Count) + 1
'If we're in the first cell must have gone to the next Area.
If SelectionCellIndex = 1 Then
SelectionAreaIndex = (SelectionAreaIndex Mod Selection.Areas.Count) + 1
End If
Selection.Areas(SelectionAreaIndex).Cells(SelectionCellIndex).Activate
'Because it will get incremented next time round
SelectionAreaIndex = SelectionAreaIndex - 1
End Sub
The code above uses the Mod function to cycle back to the beginning of the next Area or of the entire Selection.
The code to cycle backwards was surpisingly more difficult. Mod didn’t work in reverse, For/Next doesn’t work in reverse, and the beginning number is the last number of the previous Area changes (not 1). So although the logic was the same, the coding was much trickier.
Putting this in a UserForm had the additional complication that the procedures assigned to a key using OnKey have to reside in a regular module. So my code has two very short routines that poke a property in the UserForm, effectively passing the action back into the form as soon as possible.
I also learned VBA’s AppActivate command which shifts the focus from the form to Excel proper. I do that at the end of the forms Activate procedure, since it’s a Viewer after all and doesn’t need the focus.
The ActiveCell Viewer
You may be wondering why you even need an ActiveCell viewer. Well, you probably don’t. But if you did, it might be to see what’s in cells with big blobs of text, or ones that are in hidden columns or rows. And its ability to view cell contents using different format strings might also be nice:
Download
Download the sample workbook and try it yourself!
I made a form many years ago so I could see values/formats/formulas of cells offscreen/another sheet/another file. Kind of clunky, but it was handy to compare visible and nonvisible cells. It uses an app class version of SelectionChange though.
Hey Andrew, thanks for dropping by. Mine uses an application-level event class too, with the added bonus described in this post.