Mastering Excel 2003 Programming with VBA phần 4 ppt

61 261 0
Mastering Excel 2003 Programming with VBA phần 4 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

4281book.fm Page 164 Sunday, February 29, 2004 5:12 PM 164 CHAPTER 8 THE MOST IMPORTANT OBJECT You can apply many different techniques for moving around a worksheet. I’ll present a few here that I’ve used successfully for many different purposes. The two primary ways to move about a work- sheet are using the Cells property of the Worksheet object and the Offset property of the Range object. I’ve already talked about the Cells property earlier in the chapter (see Listing 8.2), so let’s take a look at the Offset property. Offset Is for Relative Navigation You can use the Offset property of the Range object to refer to ranges on a worksheet based on, or relative to, another range. This property provides you with a great deal of flexibility for moving around a worksheet. One thing that you can do with Offset is process a structured list. By structured list, I mean a list of items on a worksheet in which you know the order or structure of the columns ahead of time. Consider the list of items shown in Figure 8.8. One way you could process this list is by setting a reference to the first column of the first row in the list. Then you could loop through the list, advancing your reference down a row, and terminating the loop when you reach an empty row (assuming you know that there won’t be any empty rows within the boundaries of your list). As you loop through the list, you could investigate columns of interest by using the Offset property. Listing 8.6 demonstrates how you could filter this list. You’ll have to bear with me and pretend that Excel doesn’t have any native filtering functionality (which we’ll examine in the next chapter). Anyway, Listing 8.6 uses the Offset property to process the list shown in Figure 8.8 so that it hides any rows that contain cars from the 20th century. Further, this process highlights the mileage column if the car has less than 40,000 miles. Figure 8.8 A simple worksheet list 4281book.fm Page 165 Sunday, February 29, 2004 5:12 PM 165 FINDING MY WAY Listing 8.6: List Processing with the Offset Property Sub ListExample() FilterYear 2000 End Sub Sub Reset() With ThisWorkbook.Worksheets("List Example") .Rows.Hidden = False .Rows.Font.Bold = False .Rows(1).Font.Bold = True End With End Sub Sub FilterYear(nYear As Integer) Dim rg As Range Dim nMileageOffset As Integer ' 1st row is column header so start ' with 2nd row Set rg = ThisWorkbook.Worksheets("List Example").Range("A2") nMileageOffset = 6 ' go until we bump into first ' empty cell Do Until IsEmpty(rg) If rg.Value < nYear Then rg.EntireRow.Hidden = True Else ' check milage If rg.Offset(0, nMileageOffset).Value < 40000 Then rg.Offset(0, nMileageOffset).Font.Bold = True Else rg.Offset(0, nMileageOffset).Font.Bold = False End If rg.EntireRow.Hidden = False End If ' move down to the next row Set rg = rg.Offset(1, 0) Loop Set rg = Nothing End Sub 4281book.fm Page 166 Sunday, February 29, 2004 5:12 PM 166 CHAPTER 8 THE MOST IMPORTANT OBJECT Let me make a few comments before I analyze this listing. First, this listing uses a worksheet named “List Example” and doesn’t validate this assumption, so be sure you have either changed the worksheet name in the code or named one of your worksheets “List Example”. Then run the ListExample proce- dure to hide selected rows and the Reset procedure to display the worksheet as it originally appeared. The heart of this example is the poorly named FilterYear procedure. Notice one of the variables is named nMileageOffset. The procedure uses the Offset property to observe the value in the Mileage column. Your Range object variable, rg, is located in column 1, so to use Offset to view the Mileage column, you need to look in the cell six columns to the right. It’s a good idea, at a minimum, to store a value like this in a variable or a constant. That way if you need to change the value (perhaps you need to insert a column, for example), you only have to change it in one location. The FilterYear stores this mileage column offset in a variable named nMileageOffset. The processing loop is a Do…Loop that terminates when it finds an empty cell. Each time through the loop, you set the rg variable to refer to the next cell down. The primary assumption that is made here is that you don’t have any empty cells between the first and last row. If there is a chance that you may have empty cells, you need to use another method to process the list or put some appropriate checks in place. The first piece of business inside the loop is to see if the value in the year column (the value in the range to which the rg variable refers) is less than the value passed in the nYear parameter. If it is, you can use the EntireRow property of your Range object to refer to a range that represents all of the cells in the row occupied by the rg variable. In the same statement, set the Hidden property to true to hide the row. If the value in the Year column is equal to or greater than nYear, check the value in the Mile- age column and ensure that the row isn’t hidden. The last thing to do inside the loop is advance the rg variable so that it refers to the next cell down the worksheet. Notice in the Reset procedure that you can return a range that represents all of the rows in a work- sheet or a selected row in the worksheet. This makes it very easy to unhide all of the rows at once. In order to remove any bold formatting that the FilterYear procedure applied, remove any bold format- ting from any cell on the worksheet and then go back and apply it to the first row (the column head- ings). This is much easier and faster than looping through each cell individually and turning bold off if it is on. The output of Listing 8.6 is shown in Figure 8.9. Figure 8.9 List processing can be performed very easily with just a lit- tle bit of code. 4281book.fm Page 167 Sunday, February 29, 2004 5:12 PM 167 FINDING MY WAY Last but Not Least—Finding the End One property of the Range object that is extremely useful for navigational activities is the End prop- erty. If you are used to navigating around Excel using the Control key in conjunction with the arrow keys, you already know how End behaves—End is the programmatic equivalent of navigating using Control in conjunction with the arrow keys. If you don’t know what I’m talking about, it’ll be helpful to perform the following exercise to see for yourself. 1. On a blank worksheet in Excel, select the range A1:C10. 2. Press the numeral 1 key and then press Ctrl+Shift+Enter to populate every cell in the range with the value 1. 3. Also, place the value 1 in the cells A12:A15, B14, C12, and D13:D14, as shown in Figure 8.10. 4. Select cell A1. 5. Press Ctrl+Down Arrow to select cell A10. 6. Press Ctrl+Right Arrow to select cell C10. 7. Continue experimenting with Ctrl+(Up/Down/Right/Left) Arrow until you have a good feel for how this behaves. The general algorithm of this functionality is as follows. If the current cell is empty, then select the first nonempty cell in the direction specified by the arrow key. If a nonempty cell can’t be found, select the cell next to the boundary of the worksheet. Figure 8.10 A simple list for test- ing the End property 4281book.fm Page 168 Sunday, February 29, 2004 5:12 PM 168 CHAPTER 8 THE MOST IMPORTANT OBJECT If the current cell is not empty, then see if the cell next to the current cell (in the direction specified by the arrow key) is empty. If the next cell is empty, then select the first nonempty cell. If a nonempty cell isn’t found, select the cell next to the boundary of the worksheet. If the next cell is not empty, then select the last cell in the range of contiguous nonempty cells. Listing 8.7 presents an example that uses the End property. I set this procedure up using the worksheet from the preceding exercise. You may need to update the procedure to refer to a different worksheet if you didn’t use the first worksheet in the workbook. Listing 8.7: Using the End Property to Navigate within a Worksheet Sub ExperimentWithEnd() Dim ws As Worksheet Dim rg As Range Set ws = ThisWorkbook.Worksheets(1) Set rg = ws.Cells(1, 1) ws.Cells(1, 8).Value = _ "rg.address = " & rg.Address ws.Cells(2, 8).Value = _ "rg.End(xlDown).Address = " & rg.End(xlDown).Address ws.Cells(3, 8).Value = _ "rg.End(xlDown).End(xlDown).Address = " & _ rg.End(xlDown).End(xlDown).Address ws.Cells(4, 8).Value = _ "rg.End(xlToRight).Address = " & rg.End(xlToRight).Address Set rg = Nothing Set ws = Nothing End Sub Listing 8.7 simply uses the End property to navigate to a few locations. It outputs the address of the range it navigates to as it goes. Notice that because the End property returns a Range object, you can use it multiple times in the same statement. As you can see, using End is an efficient technique for finding the boundaries of a contiguous series of cells that contain values. It’s also useful for finding the last row in a given column or the last column in a given row. When you use End to find the last used cell in a column or row, however, you need to be wary of empty cells. In order to account for the possibility of empty cells, all you need to do to find the last cell is start at the boundary of the worksheet. So if you need to find the last row in a given column, use End, except start at the bottom of the worksheet. Similarly, to find the last column in a given row, start at the far right column of the worksheet. 4281book.fm Page 169 Sunday, February 29, 2004 5:12 PM 169 FINDING MY WAY Listing 8.8 presents two functions that return either a range that represents the last used cell in a column or the last used cell in a row, depending on which function you call. Listing 8.8: Finding the Last Used Cell in a Column or Row ' returns a range object that represents the last ' non-empty cell in the same column Function GetLastCellInColumn(rg As Range) As Range Dim lMaxRows As Long lMaxRows = ThisWorkbook.Worksheets(1).Rows.Count ' make sure the last cell in the column is empty If IsEmpty(rg.Parent.Cells(lMaxRows, rg.Column)) Then Set GetLastCellInColumn = _ rg.Parent.Cells(lMaxRows, rg.Column).End(xlUp) Else Set GetLastCellInColumn = rg.Parent.Cells(lMaxRows, rg.Column) End If End Function ' returns a range object that represents the last ' non-empty cell in the same row Function GetLastCellInRow(rg As Range) As Range Dim lMaxColumns As Long lMaxColumns = ThisWorkbook.Worksheets(1).Columns.Count ' make sure the last cell in the row is empty If IsEmpty(rg.Parent.Cells(rg.Row, lMaxColumns)) Then Set GetLastCellInRow = _ rg.Parent.Cells(rg.Row, lMaxColumns).End(xlToLeft) Else Set GetLastCellInRow = rg.Parent.Cells(rg.Row, lMaxColumns) End If End Function GetLastCellInColumn and GetLastCellInRow work almost identically, but are different in two ways. First, when you’re looking for the last used cell in a column, you need to start at the bottom of the work- sheet. For rows, you start at the far right edge of the worksheet instead. The second difference is the parameter you supply to the End property. For the last used cell in a column, you use xlUp; for the last used cell in a row you use xlToLeft. The most important statement in each of these functions is the one that uses the End property. I took the example shown here from the GetLastCellInRow function. rg.Parent.Cells(rg.Row, lMaxColumns).End(xlToLeft) 4281book.fm Page 170 Sunday, February 29, 2004 5:12 PM 170 CHAPTER 8 THE MOST IMPORTANT OBJECT The rg variable is a Range object supplied to the function as a parameter. Your objective with this statement is to move to the last possible cell in the row to which rg belongs and then use End to move to the first nonempty cell in the same row. You can see exactly how this objective is achieved by breaking the statement down from left to right: 1. First you receive a reference from rg.Parent to the worksheet that contains the range. 2. Next you use the Cells property of the worksheet object to specify the last possible cell in the row. Specify the specific cell by supplying a row number and a column number. 1. You can determine the specific row number by using the Row property of the Range object. 2. You can determine the last possible column by counting the number of columns on a worksheet. This is performed a few statements earlier and the result is assigned to the lMaxColumns variable. 3. Finally, use the End property to find the last nonempty cell in the row. This is a good example of how you can get a lot done with one statement. As you progress and get more familiar with the Excel object model, putting these statements together will become second nature for you. 99 percent of the time, this statement alone would suffice. In order to account for the possibility that the last possible cell in the row or column isn’t empty, you need to add an If…Then statement to check for this condition. If the last possible cell is nonempty and you use the End prop- erty on that cell, the function returns an incorrect result because it moves off of the cell to find the next nonempty cell. Listing 8.9 adopts these functions so that they can be called from a worksheet. The main change you need to make is to return a numeric value that can be displayed on a worksheet rather than on a Range object. Listing 8.9: Returning the Last Used Cell in a Column or Row with Worksheet Callable Functions ' returns a number that represents the last ' nonempty cell in the same column ' callable from a worksheet Function GetLastUsedRow(rg As Range) As Long Dim lMaxRows As Long lMaxRows = ThisWorkbook.Worksheets(1).Rows.Count If IsEmpty(rg.Parent.Cells(lMaxRows, rg.Column)) Then GetLastUsedRow = _ rg.Parent.Cells(lMaxRows, rg.Column).End(xlUp).Row Else GetLastUsedRow = rg.Parent.Cells(lMaxRows, rg.Column).Row End If End Function 4281book.fm Page 171 Sunday, February 29, 2004 5:12 PM 171 INPUT EASY; OUTPUT EASIER ' returns a number that represents the last ' nonempty cell in the same row ' callable from a worksheet Function GetLastUsedColumn(rg As Range) As Long Dim lMaxColumns As Long lMaxColumns = ThisWorkbook.Worksheets(1).Columns.Count If IsEmpty(rg.Parent.Cells(rg.Row, lMaxColumns)) Then GetLastUsedColumn = _ rg.Parent.Cells(rg.Row, lMaxColumns).End(xlToLeft).Column Else GetLastUsedColumn = rg.Parent.Cells(rg.Row, lMaxColumns).Column End If End Function These functions are handy because you can call them from a worksheet function. Figure 8.11 shows the results of calling these functions from the worksheet you created earlier to experiment with navigation using the Control key in conjunction with the arrow keys. Figure 8.11 The GetLastUsed- Row and GetLastUsed- Column functions can be called from a worksheet. Input Easy; Output Easier You now know everything that you need to know to learn how to collect worksheet-based input and dis- play output. Worksheet-based input/output (I/O) draws on your knowledge of using the Application, 4281book.fm Page 172 Sunday, February 29, 2004 5:12 PM 172 CHAPTER 8 THE MOST IMPORTANT OBJECT Workbook, Worksheet, and Range objects. Sure, the direct object you use is the Value property of the Range object, but you can’t effectively and professionally do this without using all of the other objects I mentioned. One of the reasons you need to draw on your knowledge of all of the objects we have covered so far is that I/O is a risky operation in terms of the potential for run-time errors and you need to pro- gram accordingly. The other reason is that without giving some thought to I/O, it’s easy to create rigid procedures that are error prone and hard to maintain. In order to avoid this problem, you should think about how your pro- cedures handle I/O and what you can do to create reasonably flexible procedures that are easy to maintain. In any event, you’ll need to make certain assumptions regarding I/O, and you need to safeguard or enforce those assumptions to eliminate or significantly reduce the potential for run-time errors. This could be as simple as telling your users to modify the workbook structure at their own risk. On the other end of the spectrum, you could develop a complex workbook and worksheet protection scheme that only allows changes that are necessary to accomplish the objectives that the workbook was designed to accomplish. Ultimately, the choice comes down to a tradeoff between development time on the one hand, and the utility of increased flexibility and application robustness on the other. That said, let’s see if I can highlight some of these tradeoffs as you explore some of the ways that you can perform I/O in Excel. Output Strategies You’ve already seen a few examples of simple, unstructured output. I’d define simple, unstructured output as output that uses the Value property of the Range object to a known worksheet without any regard for formatting and little regard for precise data placement. For example, Listing 8.2 displayed a simple grid of data to a block of cells on a worksheet. Like- wise, Listing 8.4 displayed the names in a given workbook as a simple list. Both of these examples had some output to display, and displayed it by dumping it in a range of contingent worksheet cells. Figure 8.12 A raw, unformatted report in need of some help [...]... all of the things you’d do with a range if you were doing it (i.e., using the Excel interface) manually In this chapter, you’ll learn how to use the Range object to cut, copy, paste, filter, find and sort data in Excel This chapter, when combined with the previous chapter, is really the bread and butter of Excel development After reading this chapter, if you’re comfortable with all of the topics presented,... Figure 9 .4 shows the results of running the FindExample procedure The range to search is the cur­ rent region associated with cell A1 and the product to search for is indicated in cell J1 The found list is the current region associated with cell H4 Don’t Like It? Change It with Replace Replace is a hard-working method as far as I’m concerned This is one of those features that many general Excel users... to replace null values (or empty cells) in a list with some default value such as zero For example, in Figure 9 .4 let’s replace empty cells in column B and C with “UNKNOWN” and empty cells in column D with zero 1 Select the range B2:C17 2 Press CTRL+H to view the Replace dialog box 3 Leave the Find What text box empty 4 Enter UNKNOWN in the Replace With text box 5 Click Replace All 6 Select D2:D17... replaces the formatting with italic, size 8 WOULD YOU LIKE SPECIAL SAUCE WITH THAT? Listing 9.3: Using Replace to Replace Formatting Sub ReplaceFormats() ' set formatting to look for With Application.FindFormat Font.Bold = True Font.Size = 11 End With ' set formatting that should be applied instead With Application.ReplaceFormat Font.Bold = False Font.Italic = True Font.Size = 8 End With ActiveSheet.Cells.Replace... empty string for both the What parameter and the Replacement parameter Would You Like Special Sauce with That? Are you familiar with the Go To Special functionality in Excel? This is another chunk of functionality that many Excel users either don’t know exists or don’t take advantage of Check it out in Excel; select Edit � Go To and then click the Special button at the bottom left corner of the Go To... Figure 8. 14 shows how you could name the various sections of this report Assuming you create the range names shown in Figure 8. 14, Listing 8.11 provides you with an example of a much more robust procedure This procedure uses the WorksheetExists procedure pre­ sented in the last chapter as well as the RangeNameExists procedure shown in Listing 8.5 Listing 8.11: A More Flexible Procedure for Working with. .. columns Set rg = ws.Range(ws.Cells(2, 2), ws.Cells(lLastRow, 3)) rg.Replace "", "UNKNOWN" ' Replace empty cells in 4th column Set rg = ws.Range(ws.Cells(2, 4) , ws.Cells(lLastRow, 4) ) rg.Replace "", "0" Set rg = Nothing Set ws = Nothing End Sub More times than not, when you’re working with a list, at least one column always has a value for every row in the list This is the column that you’ll want to... More than a few people have probably become attracted to the potential benefits of automation with VBA while enduring the mental pain associated with this activity If you know of a process or two that include large amounts of shuffling data around, automating these processes could be your first big win as an Excel developer Usually it’s fairly easy to automate these processes, and the time savings of... ws.Range("DATA").NumberFormat = "#,##0" If RangeNameExists(ws, "COLUMN_TOTAL") Then With ws.Range("COLUMN_TOTAL") Formula = "=SUM(R[-9]C:R[-1]C)" Font.Bold = True NumberFormat = "#,##0" End With End If If RangeNameExists(ws, "ROW_TOTAL") Then With ws.Range("ROW_TOTAL") Formula = "=SUM(RC[-12]:RC[-1])" Font.Bold = True NumberFormat = "#,##0" End With End If Set ws = Nothing End Sub Note See Listing 7.2 for the WorksheetExists... of cells bounded on all sides by empty rows and columns Take a look at Figure 9.10 No matter which cell you select in the range B2:D4, the address of the CurrentRegion is B2:D4 Similarly, the two other shaded ranges represent the CurrentRegion asso­ ciated with any cell within the shaded area When applied to a list, CurrentRegion is also handy when you are trying to determine the char­ acteristics of . 42 81book.fm Page 1 64 Sunday, February 29, 20 04 5:12 PM 1 64 CHAPTER 8 THE MOST IMPORTANT OBJECT You can apply many different. car has less than 40 ,000 miles. Figure 8.8 A simple worksheet list 42 81book.fm Page 165 Sunday, February 29, 20 04 5:12 PM 165 FINDING MY WAY Listing 8.6: List Processing with the Offset Property. B 14, C12, and D13:D 14, as shown in Figure 8.10. 4. Select cell A1. 5. Press Ctrl+Down Arrow to select cell A10. 6. Press Ctrl+Right Arrow to select cell C10. 7. Continue experimenting with

Ngày đăng: 13/08/2014, 15:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan