Working with Ranges - Diving Deeper into VBA - Excel VBA 24-Hour Trainer (2015)

Excel VBA 24-Hour Trainer (2015)

Part II
Diving Deeper into VBA

Lesson 8
Working with Ranges

The Range object is probably the most frequently used object in VBA. Almost anything you do in a worksheet where VBA is concerned involves either a cell (or range of cells), or a reference of some kind to a range location that helps to direct whatever action your macro is undertaking. When you programmatically create a chart, modify a pivot table's source data, or insert picture files or comments, you are working with ranges.

Because ranges are so commonly referred to in code, this lesson introduces you to various syntaxes you will soon become familiar with, and in fact depend on, to refer to or manipulate Range objects. My approach with this lesson is to demonstrate basic code lines with pictures to show how ranges can be identified or selected.

As you'll hear over and over in VBA programming circles, you need not, and normally will not, actually select a range in order to work with it. You can refer to and manipulate (such as by editing or formatting) ranges of cells on other worksheets or other workbooks without leaving your active worksheet. The pictures in this lesson show the selection of ranges for visual confirmation of the code at work, but after this lesson, you will rarely see code that selects or activates an object.

Working with Contiguously Populated Ranges

The simplest ranges to deal with are those that have all cells filled with data or formulas, and no empty cells within that range. Figure 8.1 shows a typical-looking list of data for which you can easily identify its last row, its last column, and its address. Based on Figure 8.1, the variables in the following macro yield 6 for the LastRow variable, and 3 (column C, the third column in the spreadsheet grid) for the LastColumnvariable:

Sub Find_LastRow_LastColumn()

Dim LastRow As Long, LastColumn As Long

LastRow = Cells(Rows.Count, 1).End(xlUp).Row

LastColumn = Cells(2, Columns.Count).End(xlToLeft).Column

MsgBox _

"The last row is: " & LastRow & vbCrLf & _

"The last column is: " & LastColumn

End Sub

image

Figure 8.1

Using the Cells Property

You have seen with the previous example and in other VBA expressions that the Cells property can select or refer to a range. The Cells range syntax is Cells(RowIndex, ColumnIndex). Therefore, the expression Cells(2, 5) refers to cell E2 because that is the same as row 2 of column 5. For the Cells property, the row component must be a numeral, but the column can be a letter that must be in quotes, for example Cells(2, "E"). Therefore, in practice, either of these expressions bold cell E2:

Cells(2, 5).Font.Bold = True

Cells(2, "E").Font.Bold = True

You can incorporate two Cells properties within the Range statement to refer to a range larger than just one cell. Using the example variables for LastRow and LastColumn, the following line of code tells you the range address of the list shown in Figure 8.1:

MsgBox Range(cells(1, 1), Cells(LastRow, LastColumn)).Address

As a final example, you can use Cells to select a particular range of cells, such as D3:F5 in Figure 8.2:

Range(cells(3, 4), cells(5, 6)).select

image

Figure 8.2

Using CurrentRegion

The CurrentRegion property refers to a localized range of contiguous data that may exist among other ranges on that worksheet containing a mix of filled and empty cells. Suppose you have disjointed data on your worksheet, as depicted in Figure 8.3. With the active cell in the local (CurrentRegion) area of the range of data you want to work with, the following line of code will select that active cell's CurrentRegion, as shown in Figure 8.4.

ActiveCell.CurrentRegion.Select

image

Figure 8.3

image

Figure 8.4

Working with Noncontiguously Populated Ranges

You will often need to locate or refer to ranges that are broken up by empty cells, usually referred to as noncontiguous ranges. VBA offers some clever options for taming the noncontiguous range beast.

Using Range with Several Cells

As shown in Figure 8.5, you can select various cells in a union with this example code line:

Range("B2, D5, F1:F4").Select

image

Figure 8.5

Notice the construction has cell addresses, or ranges, separated by a comma and a space, enclosed in quotes.

Using OFFSET

The OFFSET property refers to a range by adding or subtracting (offsetting) row and column numbers from a relative reference to refer to a new range. In Figure 8.6, the active cell is B4.

image

Figure 8.6

If you want to select a range that is relative to the active cell by extending the range upward 2 rows and outward 4 columns, you can use the following code line. The result is shown in Figure 8.7.

Range(activecell, activecell.Offset(-2, 4)).Select

image

Figure 8.7

Using RESIZE

The RESIZE property changes the size of a range, based on a cell of interest as the reference point. In this example, range B3 is resized by 4 rows and 5 columns, thereby selecting range B3:F6. (See Figure 8.8.) The code line that is used in this example is

Range("B3").Resize(4, 5).Select

image

Figure 8.8

Identifying a Data Range

In some cases you will only want to identify a range of cells that contain data or formulas, but not formatting. In Figure 8.9, cell H3 is a lonely soul, apart from the data range but formatted with red fill color for demonstration purposes.

image

Figure 8.9

This example shows how to select a data range on the current sheet, starting at cell A1, and display the address of the range to the user. The data range does not include cells that are formatted that do not contain data. To get the data range, this example finds the last row and the last column that contain actual data by using the Find method of the Range object:

Sub SelectDataRange()

Dim LastRow As Long, LastColumn As Long

LastRow = Cells.Find(What:="*", SearchDirection:=xlPrevious, _

SearchOrder:=xlByRows).Row

LastColumn = Cells.Find(What:="*", SearchDirection:=xlPrevious, _

SearchOrder:=xlByColumns).Column

Range("A1").Resize(LastRow, LastColumn).Select

MsgBox "The data range address is " & Selection.Address(0, 0) & ".", _

vbInformation, "Data-containing range address:"

End Sub

Identifying the UsedRange

The UsedRange property represents cells on a worksheet that are currently being used or have been used. This includes formatted cells that do not contain data, such as what's shown in Figure 8.10.

image

Figure 8.10

This example shows how to select the UsedRange on the current worksheet by using the UsedRange property of the Worksheet object and the Select method of the Range object. The selected address of the worksheet's UsedRange is displayed in a message box:

Sub SelectUsedRange()

ActiveSheet.UsedRange.Select

MsgBox "The used range address is " & _

ActiveSheet.UsedRange.Address(0, 0) & ".", 64, "Used range address:"

End Sub

Finding the Dynamic Last Rows and Columns

This section includes a collection of several dynamic row and column locations wrapped into one macro example. You may need to not only find the last row of data, but limit your search to a particular set of columns. The same goes for the last used column, based on one row, all rows, or a specific range of rows. Figure 8.11 shows the versatility of the following macro for handling all these scenarios:

Sub DataRangeLastRowsColumns()

'Declare variables for last rows and columns

Dim LastRow As Long, LastColumn As Long

Dim LastRowSingleColumn As Long, LastRowSomeColumns As Long

Dim LastColumnSingleRow As Long, LastColumnSomeRows As Long

'Last row of data considering all columns.

LastRow = Cells.Find(What:="*", After:=Range("A1"), _

SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

'Last row of data considering just column D.

LastRowSingleColumn = Cells(Rows.Count, 4).End(xlUp).Row

'Last row of data considering just columns B, C, and D.

LastRowSomeColumns = Range("B:D").Find(What:="*", After:=Range("B1"), _

SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

'Last column of data considering all rows.

LastColumn = Cells.Find(What:="*", After:=Range("A1"), _

SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column

'Last column of data considering just row 3.

LastColumnSingleRow = Cells(3, Cells.Columns.Count).End(xlToLeft).Column

'Last column of data considering just rows 1, 2, and 3.

LastColumnSomeRows = Rows("1:3").Find(What:="*", _

After:=Cells(1, Cells.Columns.Count), _

SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column

'Advise the user of last row and column information.

MsgBox _

"Last row of data anywhere: " & LastRow & vbCrLf & _

"Last row of data in column D: " & LastRowSingleColumn & vbCrLf & _

"Last row of data among columns B, C, and D: " & _

LastRowSomeColumns & vbCrLf & vbCrLf & _

"Last column of data anywhere: " & LastColumn & vbCrLf & _

"Last column of data in row 3: " & LastColumnSingleRow & vbCrLf & _

"Last column of data among rows 1, 2, and 3: " & LastColumnSomeRows, , _

"Last row and last column information:"

End Sub

image

Figure 8.11

Identifying Where the Range Starts and Ends When No Start or End Point Is Known

It probably seems that cell A1 is where data starts on a worksheet. Row 1 is popular for header labels in a list, and column A is the leftmost column on the spreadsheet grid, prominently visible. But sometimes data finds itself on a worksheet in areas you would not expect, and the next day, that same worksheet can hold data somewhere totally different. You need a catch-all macro to find the range of data, from wherever it starts to wherever it ends.

This example shows how to select a data range on the current sheet when you do not know the starting or ending location and display the range address in a message box. The data range does not include cells that are formatted. This example finds the first and last row and column that contain actual data by using the Find method of the Range object. The result is shown in Figure 8.12.

Sub UnknownRange()

Dim FirstRow As Long, FirstCol As Long, LastRow As Long, LastCol As Long

Dim myUsedRange As Range

FirstRow = _

Cells.Find(What:="*", SearchDirection:=xlNext, SearchOrder:=xlByRows).Row

FirstCol = _

Cells.Find(What:="*", SearchDirection:=xlNext, SearchOrder:=xlByColumns).Column

LastRow = _

Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows).Row

LastCol = _

Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByColumns).Column

Set myUsedRange = Range(Cells(FirstRow, FirstCol), Cells(LastRow, LastCol))

myUsedRange.Select

MsgBox _

"The data range on this worksheet is " & _

myUsedRange.Address(0, 0) & ".", vbInformation, "Range address:"

End Sub

image

Figure 8.12

Try It

In this lesson, you see how to create a macro that identifies the location of a chart on a worksheet. The purpose of the exercise is to demonstrate how to identify an object's location without selecting or activating any ranges or objects. The value of the exercise is to know with confidence where else on a worksheet (that is, below or to the right of an object) you can insert a new object, edit a cell, or take some action on the worksheet without coming into contact with the existing object of interest.

Lesson Requirements

To get the sample workbook file, you can download Lesson 8 from the book's website at www.wrox.com/go/excelvba24hour.

Hints

Using the Index property of the embedded chart helps avoid needing to know the Chart object's name.

The Cells property is especially useful in this example, when variables are declared to identify row and column locations.

Step-by-Step

1. Open an Excel workbook in which you have a Chart object on a worksheet. If you don't have such a workbook, in a new worksheet, construct a simple table and insert a chart similar to Figure 8.13.

2. Compose a macro to tell you the location of the chart on your worksheet. You will want to identify the top and bottom rows, and the left and right columns that the Chart object covers.

3. From the Developer tab on the Ribbon, click the Visual Basic icon, or press Alt+F11 on your keyboard to go to the Visual Basic Editor.

4. From the VBE menu bar, click InsertModule.

5. In your new module, type the name of the macro. In this example, the macro is named Sub ChartLocation().

6. Declare variables for the top and bottom rows, and left and right columns that the chart touches:

7. Dim TopRow As Long, BottomRow As Long

Dim LeftColumn As Long, RightColumn As Long

7. Open a With structure for the ChartObject. Because there is only one chart on the worksheet, its Index property is 1 and you can refer to it in code with this statement:

With ActiveSheet.ChartObjects(1)

8. Declare your row and column variables like so:

9. TopRow = .TopLeftCell.Row

10. BottomRow = .BottomRightCell.Row

11. LeftColumn = .TopLeftCell.Column

RightColumn = .BottomRightCell.Column

9. Close the With structure:

End With

10.Utilizing the variables to show an example of changing a cell outside the range occupied by the chart, this line of code enters the word Hello into a cell two rows below and two columns to the right of the bottom-right corner of the chart:

Cells(BottomRow + 2, RightColumn + 2).Value = "Hello"

11.For demonstration purposes, an optional enhancement to this macro is the following message box code that confirms the chart's location when the macro is run, as shown in Figure 8.14:

12. MsgBox "Top row: " & TopRow & vbCrLf & _

13. "Bottom row: " & BottomRow & vbCrLf & _

14. "Left column: " & LeftColumn & vbCrLf & _

"RightColumn: " & RightColumn, , "ChartLocation"

12.End the macro with the End Sub line.

13.Go ahead and test your macro. Press Alt+Q to exit the VBE, and from your worksheet press Alt+F8 to show the Macro dialog box. Select the macro name and click the Run button. The ChartLocation macro looks like this in its entirety:

14. Sub ChartLocation()

15. Dim TopRow As Long, BottomRow As Long

16. Dim LeftColumn As Long, RightColumn As Long

17. With ActiveSheet.ChartObjects(1)

18. TopRow = .TopLeftCell.Row

19. BottomRow = .BottomRightCell.Row

20. LeftColumn = .TopLeftCell.Column

21. RightColumn = .BottomRightCell.Column

22. End With

23. Cells(BottomRow + 2, RightColumn + 2).Value = "Hello"

24. MsgBox "Top row: " & TopRow & vbCrLf & _

25. "Bottom row: " & BottomRow & vbCrLf & _

26. "Left column: " & LeftColumn & vbCrLf & _

27. "RightColumn: " & RightColumn, , "ChartLocation"

End Sub

image

Figure 8.13

image

Figure 8.14

REFERENCE Please select the video for Lesson 8 at www.wrox.com/go/excelvba24hour. You will also be able to download the code and resources for this lesson from the website.