Changing Text Case in the Excel worksheet using VBA
This sub will help speed up the process of changing text case on your worksheets. As you know, there are at least four different text cases (if you know others, do let me know).
- UPPER Case
- lower case
- Proper Case
- Sentence case
I have affected the above text accordingly, to demonstrate each case. Normally I recommend using a helper column and putting the case formulas directly onto the worksheet, rather than a VBA solution, but if you are pressed for time or already doing text manipulation in your code (for example, as part of a userform), this may help.
The first thing I'll do at the top of a new standard module is set up a global boolean variable. I do this in case I want to use this code in another procedure, to test if it was successful. It's a perfect example of modular programming and making smaller, standalone functions which can be easily cut and pasted into new projects. I'll demonstrate a sample of this below, after showing you the main code.
Dim bWasDone As Boolean
And here is the code. it accepts two arguments:
- A string variable representing a valid range
- A number representing the Case option. This is optional, but if you don't specify a case, it assumes you want upper case.
Sub FixText(rng As String, Optional lType As Long) ' ' lType can be any one of these parameters: ' 1 - Convert to UPPER CASE ' 2 - Convert to lower case ' 3 - Convert to Proper Case ' 4 - Convert to Sentence case ' Dim rngSource As Excel.Range Dim cell As Excel.Range Application.ScreenUpdating = False ' test for valid range On Error Resume Next Set rngSource = Range(rng) On Error GoTo 0 If TypeName(rngSource) = "Nothing" Then bWasDone = False GoTo ExitProc End If If TypeName(rngSource) <> "Range" Then bWasDone = False GoTo ExitProc End If ' if ltype was left blank, do upper case by default If lType = 0 Then lType = 1 Select Case lType Case 1 ' upper case For Each cell In rngSource.SpecialCells(xlCellTypeConstants, 2) cell = UCase$(cell) Next cell Case 2 ' lower case For Each cell In rngSource.SpecialCells(xlCellTypeConstants, 2) cell = LCase$(cell) Next cell Case 3 ' proper case For Each cell In rngSource.SpecialCells(xlCellTypeConstants, 2) If Len(cell) > 2 Then cell = StrConv(cell, vbProperCase) End If Next cell Case 4 ' sentence case ' code taken from http://vbamacros.blogspot.com/2007_09_01_archive.html For Each cell In rngSource.SpecialCells(xlCellTypeConstants, 2) s = cell.Value Start = True For i = 1 To Len(s) ch = Mid$(s, i, 1) Select Case ch Case "." Start = True Case "?" Start = True Case "a" To "z" If Start Then ch = UCase$(ch) Start = False Case "A" To "Z" If Start Then Start = False Else ch = LCase$(ch) End If End Select Mid$(s, i, 1) = ch Next i cell.Value = s Next cell Case Else ' default to upper case For Each cell In rngSource.SpecialCells(xlCellTypeConstants, 2) cell = UCase$(cell) Next cell End Select bWasDone = True ExitProc: Set rngSource = Nothing Application.ScreenUpdating = True End Sub
First the code checks to make sure it was passed a valid range. It tries to set the string to a Range object variable. If it didn't work, we exit the procedure. If the lType argument (optional) was left out, or if someone tries to be funny and types something else there, we default to upper case (1). Then depending on what was typed, we loop through each Constant cell in the range and perform the case changing procedure.
Notice that I use a For Each loop, this is faster than a counter loop (ex: For i = 1 to rngSource.Cells.count).
We use the SpecialCells property to limit the cells we act on to just the constants (text). The number 2 after 'xlCellTypeConstants' means the second option from the Go To Special dialog box, after you click 'Constants'.
Also notice that I use the string versions of Mid, UCase, LCase. If you check the help for these functions, it says "Returns a Variant (String)" - if you see this, that means you can append a '$' to the end of the function. Since we want these functions to return a string (not a variant), we use the '$' versions to make the code run significantly faster, because VBA isn't converting the String to Variant and back to a String.
The end result is significantly streamlined, compact and fast code.
If you wanted to keep this code hidden from others, there is no need to use the Private keyword in the first line of the macro, because any macro with arguments is automatically made Private, even if all of the arguments are Optional. Otherwise, how would you pass the arguments to it?
Here is a sample showing how to implement the code. Since the range must be passed as a string, it must be in quotes. Remember:
- FixText "A1:A10"
and
- FixText "A1:A10", 1
are the same.
Sub TestOutMyNewMacro() FixText "A1:A10", 4 End Sub
And if I wanted to use the global boolean variable I mentioned above, it would go something like this:
Dim bWasDone As Boolean Sub TestOutMyNewMacro() FixText "A1:A10", 4 If bWasDone = True Then MsgBox "It worked!" Else MsgBox "It failed." End If End Sub
When FixText is done, it will have set the bWasDone variable to False if it failed, otherwise it was set to True. Since it is declared at the top of the module, outside of any procedure, as a global variable it retains its value after the FixText procedure ends, and it is available to be checked by the TestOutMyNewMacro procedure. Keep in mind all three sets of code (the global boolean variable bWasDone, the FixText procedure, and the TestOutMyNewMacro procedure were all located in the same module, for demonstration purposes.
LAST UPDATED: March 26, 2008