Highlight rows if value in column A is x - vba

How can I highlight a single row a color if text in column A = X
Using Row 4 as an example:
What i'm ultimately trying to get is if Cell in Column A is = X then change row color from Range("B4:N4") to Black And Text.Color to White from Range("F4:N4")
Ultimately I would want it to be something like Range(Cells(i, "B"), Cells(LastRow, LastCol)) but only color one row.
This is what i am working with so far.
Sub Header()
Application.ScreenUpdating = False
Dim sht2 As Worksheet
Set sht2 = ThisWorkbook.Worksheets("Email Form")
sht2.Activate
sht2.Unprotect
Dim LastRow As Long, LastCol As Long
Dim rng As Range, c As Range
Dim WholeRng As Range
Dim i As Integer
On Error GoTo 0
With sht2
Set rng = .Cells
LastRow = rng.Find(What:="*", After:=rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
LastCol = rng.Find(What:="*", After:=rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
'MsgBox wholerng.Address
Set WholeRng = Range(Cells(i, "B"), Cells(LastRow, LastCol)).Rows
For i = 4 To LastRow
If sht2.Cells(i, 1).Value = "X" Then
With WholeRng
With .Interior
.PatternColorIndex = xlAutomatic
.Color = 1
.TintAndShade = 0
.Font.Color = 0
End With
End With
End If
Next i
Dim b As Boolean
For Each rng In WholeRng.Rows
If Not rng.Hidden Then
If b Then rng.Interior.Color = 1
b = Not b
End If
Next
End With
Set sht2 = Nothing
Set rng = Nothing
Set WholeRng = Nothing
Application.ScreenUpdating = False
End Sub

VBA Conditional Formatting.
Option Explicit
Sub Header()
Dim sht2 As Worksheet
Dim firstRow As Long, lastRow As Long, lastCol As Long
'Application.ScreenUpdating = false
On Error GoTo 0
Set sht2 = ThisWorkbook.Worksheets("Email Form")
firstRow = 4
With sht2
.Activate
.Unprotect
lastRow = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
lastCol = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
'black row, white text B:N
With .Range(.Cells(firstRow, "B"), .Cells(lastRow, lastCol))
'optionally remove any pre-existing CFRs
.FormatConditions.Delete
With .FormatConditions.Add(Type:=xlExpression, Formula1:="=UPPER($A4)=CHAR(88)")
.Interior.ThemeColor = xlThemeColorLight1
.Font.ThemeColor = xlThemeColorDark1
.SetFirstPriority
.StopIfTrue = False
End With
End With
'don't display values from B:E
With .Range(.Cells(firstRow, "B"), .Cells(lastRow, "E"))
With .FormatConditions.Add(Type:=xlExpression, Formula1:="=UPPER($A4)=CHAR(88)")
.NumberFormat = ";;;"
End With
End With
'I tnhink you want to reProtect the worksheet here
.Protect
End With
Application.ScreenUpdating = True
End Sub

I think you can achieve your goal using Conditional Formatting:
You can create a condition for each format setting for the two different ranges.
Select one range at a time, then from the Home tab, create a New Conditional Formatting Rule, choose to Use a Formula and then enter a formula like:
=$A2="X"
Note that when using relative/mixed references in conditional formatting, it will be compared to the first cell in the range you are working with. I've selected range B2:N7 to apply formatting to, so the mixed reference needs to be created as it should apply to the B2 cell. You can't see it, but the reference automatically changes for all other cells in the same range, the same as if you were filling a formula across the rest of the range. For example, the formatting for the K5 cell will be dependent on the value in $A5 (because the column reference is fixed but the row reference is dynamic).
Then set the background colour or font colour you want for the range specified. This condition will check column A of the corresponding row.

I re-wrote some of your code and added comments to show you why. But by and large, I followed your original approach.
Sub Header()
Dim Sht2 As Worksheet
Dim LastRow As Long, LastCol As Long
Dim IsBlack As Boolean, FillPattern As Long
Dim Rng As Range
Dim R As Long
' Set sht2 = ThisWorkbook.Worksheets("Email Form")
Set Sht2 = ThisWorkbook.Worksheets("Taylor")
' On Error GoTo 0 ' this is the default: no need to set
Application.ScreenUpdating = False
With Sht2
.Activate ' no need to activate this sheet
.Unprotect
' this is the whole sheet: Easier to refer to it as .Cells
' Set rng = .Cells
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
' LastRow = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, _
' LookIn:=xlFormulas, SearchOrder:=xlByRows, _
' SearchDirection:=xlPrevious, MatchCase:=False).Row
' LastCol = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, _
' LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
' SearchDirection:=xlPrevious, MatchCase:=False).Column
' MsgBox "Last row = " & LastRow & vbCr & _
' "Last column = " & LastCol
For R = 4 To LastRow
IsBlack = Not CBool(StrComp(.Cells(R, 1).value, "X", vbTextCompare))
FillPattern = CLng(Array(xlNone, xlSolid)(Abs(IsBlack)))
Set Rng = .Range(.Cells(R, 1), .Cells(R, LastCol))
With Rng.Interior
If .Pattern <> FillPattern Then
.Pattern = FillPattern
If IsBlack Then
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorLight1
End If
.TintAndShade = 0
.PatternTintAndShade = 0
Rng.Font.ColorIndex = Array(xlAutomatic, 2)(Abs(IsBlack))
End If
End With
Next R
End With
' VBA does this cleanup automatically at the end of the sub
' Set sht2 = Nothing
' Set Rng = Nothing
Application.ScreenUpdating = False
End Sub

Related

Inserting Range into Array in VBA for iteration

I am facing some issues with VBA. Let me explain what I am trying to achieve. I have 2 sheets in 1 workbook. They are labelled "Sheet1" and "Sheet2."
In "Sheet1," there are 100 rows and 100 columns. In column A, it is filled with eg: SUBJ001 all the way to SUBJ100. In "Sheet2," there is only 1 Column A, with a range of rows. Eg: "SUBJ003, SUBJ033, SUBJ45." What I am trying to achieve is to use my mouse, highlight the column A in "Sheet2," and compare each individual cell with the cells in column A. Should there be a match, it will copy the entire row and paste them in a new sheet that the macro creates in the same workbook. However, i am experiencing an out of range error at Set Rng =.Find(What:=Arr(I), ... Thanks!
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set Rng = Application.InputBox("Select target range with the mouse", Type:=8)
MyArr = Rng
Set NewSh = Worksheets.Add
With Sheets("Sheet1").Range("A:A")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
Rng.EntireRow.Copy NewSh.Range("A" & Rcount)
' Use this if you only want to copy the value
' NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
MyArr = Rng is setting MyArr to be a two-dimensional array where the first rank corresponds to the rows in Rng and the second rank corresponds to the columns in Rng.
Assuming you only have one column in Rng, then your Find statement should refer to the values in that first column using MyArr(I, 1), i.e.
Set Rng = .Find(What:=MyArr(I, 1), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)

How can I color every other row, but skip rows marked X?

How can I color every other row but skip any row where Column A = X?
Whats wrong is it colors over my sub heading rows. I am trying to get it to skip the heading rows which is marked by an invisible X in Column A.
Can it skip the sub headings and the row below the sub heading row be white? Kind of like its starting over again.
This is the code I have that colors rows white then gray to the end for the entire range:
Sub Format()
Application.ScreenUpdating = False
Dim sht2 As Worksheet
Set sht2 = ThisWorkbook.Worksheets("Email Form")
sht2.Activate
sht2.Unprotect
Dim LastRow As Long, LastCol As Long
Dim rng As Range
Dim WholeRng As Range
With sht2
Set rng = Cells
LastRow = rng.Find(What:="*", After:=rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
LastCol = rng.Find(What:="*", After:=rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
Set WholeRng = Range(Cells(4, "B"), Cells(LastRow, LastCol))
WholeRng.Select
With WholeRng
With .Interior
.PatternColorIndex = xlAutomatic
.Color = RGB(255, 255, 255)
.TintAndShade = 0
Range(Cells(4, "B"), Cells(LastRow, LastCol)).Borders(xlInsideHorizontal).LineStyle = xlContinuous
Range(Cells(4, "B"), Cells(LastRow, LastCol)).Borders(xlInsideVertical).LineStyle = xlContinuous
Range(Cells(4, "B"), Cells(LastRow, LastCol)).Borders(xlEdgeBottom).LineStyle = xlContinuous
Range(Cells(4, "B"), Cells(LastRow, LastCol)).Borders(xlEdgeRight).LineStyle = xlContinuous
End With
End With
Dim b As Boolean
For Each rng In WholeRng.Rows
If Not rng.Hidden Then
If b Then rng.Interior.Color = Black
b = Not b
End If
Next
End With
Set rng = Nothing
Set WholeRng = Nothing
Set sht2 = Nothing
Application.ScreenUpdating = True
End Sub
You could expand your current if statement, using the and operator.
Example:
Dim b As Boolean
For Each rng In WholeRng.Rows
If Not rng.Hidden Then
' UPDATED LINE BELOW.
If b And sht2.Cells(rng.Row, 1) <> "x" Then rng.Interior.Color = Black
b = Not b
End If
Next
The code extracts the current row number from the rng object. It uses that to peek at the contents of column a.
An alternative approach is to use Excel's built-in conditional formatting. This is probably the easier method.

excel vba select last row and last column

I am trying to select from A9 to the lastrow & lastcolumn.
I have this to select the last cell, but it doesn't select from A9 to Last it just selects the lastrow/lastcolumn. Also this is not ideal because if I have blanks in the future.
I have searched and could not find anything for selecting from a cell to the lastrow & lastcolumn
Sub FindLast()
Application.ScreenUpdating = False
Range("A9").End(xlToRight).End(xlDown).Select
Application.ScreenUpdating = True
End Sub
Search order in my file would be Column A & Row 8 if that helps at all.
Code Below is what I am using to work on active sheets
Sub SelectAll()
Application.ScreenUpdating = False
Dim lastRow As Long, lastCol As Long
Dim Rng As Range
Dim WholeRng As Range
With ActiveWorksheet
Set Rng = Cells
'last row
lastRow = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
'last column
lastCol = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
Set WholeRng = Range(Cells(9, "A"), Cells(lastRow, lastCol))
WholeRng.Select
End With
Application.ScreenUpdating = True
End Sub
Or you could exploit UsedRange
Sub FindLast()
With Activesheet
.Range(.Range("A9"), .UsedRange.Cells(.UsedRange.Rows.Count, .UsedRange.Columns.Count)).Select
End With
End Sub
The safest way is use the Find function:
Option Explicit
Sub LastRow_Col_Find()
' Safest way to ensure you got the last row:
Dim lastRow As Long, lastCol As Long
Dim Rng As Range
Dim WholeRng As Range
With Worksheets("report")
Set Rng = .Cells
' Safest way to ensure you got the last row
lastRow = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
'MsgBox lastRow ' <-- for DEBUG
' Safest way to ensure you got the last column
lastCol = Rng.Find(What:="*", After:=Rng.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
'MsgBox lastColumn ' <-- for DEBUG
' set the Range for the entire UsedRange in "YourSheetName" sheet
Set WholeRng = .Range(.Cells(9, "A"), .Cells(lastRow, lastCol))
WholeRng.Select '<-- ONLY IF YOU MUST
End With
End Sub

Selecting only Cells with Value VBA

I have the code below and works fine, but I only want to copy cells with Values. I have blank data in the middle, as I will delete that does not make sense to copy them too.
Sub FindAgain()
'
' FindAgain Macro
'
Dim Ws As Worksheet
Dim LastRow As Long
AC = ActiveCell.Column
Set Ws = Worksheets("Sheet1")
LastRow = Ws.Cells(Rows.Count, "B").End(xlUp).Row
Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveCell.Offset(1, 0).Select
Range(ActiveCell, Cells(LastRow, AC)).Select
End Sub
Any idea how I can better write it? With Loop maybe? Thanks!
I assume that after Range(ActiveCell, Cells(LastRow, AC)).Select you see a region selected that you want to copy ignoring blank cells. One way to go about it is to iterate over all the cells in Selection, check if they are not empty and copy them:
Dim c As Range
Dim i As Long
' store current row for every column separately
Dim arrRowInCol() As Long
ReDim arrRowInCol(Selection.Column To Selection.Column + Selection.Columns.Count - 1)
For i = LBound(arrRowInCol) To UBound(arrRowInCol)
' init the first row for each column
arrRowInCol(i) = Selection.Row
Next i
For Each c In Selection
If Len(Trim(c)) <> 0 Then
c.Copy Destination:=Sheets("Sheet2").Cells(arrRowInCol(c.Column), c.Column)
arrRowInCol(c.Column) = arrRowInCol(c.Column) + 1
End If
Next c
Found a way to do what I want: At least is working, i am newby so, for you guys may seem funny or bad, for me is great =D
Sub FindAgain()
'
' FindAgain Macro
'
Dim Ws As Worksheet
Dim LastRow As Long
Dim c As Range
Dim i As Integer
Dim j As Integer
AC = ActiveCell.Column
Set Ws = Worksheets("Sheet1")
LastRow = Ws.Cells(Rows.Count, "B").End(xlUp).Row
i = 15
j = 7
Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveCell.Offset(1, 0).Select
Range(ActiveCell, Cells(LastRow, AC)).Select
For Each c In Selection
If Len(Trim(c)) <> "" Then
c.Copy Destination:=Sheets("Sheet1").Cells(i, j)
End If
If c = "" Then
i = i
Else
i = i + 1
End If
j = j
Next c
End Sub
I will start with your code, which actually tries to select the ranges. This is what I have built upon it:
Option Explicit
Public Sub FindMe()
Dim my_range As Range
Dim temp_range As Range
Dim l_counter As Long
Dim my_list As Object
Dim l_counter_start As Long
Set my_list = New Collection
l_counter_start = Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Row + 1
For l_counter = l_counter_start To Worksheets("sheet1").Cells(Rows.Count, "B").End(xlUp).Row
If Cells(l_counter, 2) <> "" Then my_list.Add (l_counter)
Next l_counter
For l_counter = 1 To my_list.Count
Set temp_range = Range(Cells(my_list(l_counter), 2), Cells(my_list(l_counter), 4))
If my_range Is Nothing Then
Set my_range = temp_range
Else
Set my_range = Union(my_range, temp_range)
End If
Next l_counter
my_range.Select
End Sub
It works upon a scenario like this:
Pretty much it works like this:
We declare two ranges.
The range my_range is the one to be selected at the end.
The range temp_range is only given, if there is a value in the second column.
Then there is a union of both ranges, and my_range is selected at the end of the code.

VBA: Trying to consolidate all worksheets into one new worksheet in single workbook

I am trying to copy all worksheets, one at a time, and pasting into a new worksheet. These files come from multiple third parties so the worksheets can vary. I'm running into a problem below when trying to determine last row Lrow and last column Lcol because an error appears saying Object doesn't support this property or method. I do plan on submitting this to my work so any help with error proofing or general macro tips are appreciated.
Sub ws_copy()
Dim Lrow As Long
Dim Lcol As Long
Dim Pasterow As Long
Dim WSCount As Integer
Dim i As Integer
'On Error Resume Next
'Application.DisplayAlerts = False
i = Application.InputBox(prompt:="Enter the place order of first tab to be copied.", Title:="Worksheet Consolidation", Type:=1)
If IsEmpty(i) = True Then
Exit Sub
Else
If IsNumeric(i) = False Then
MsgBox "Enter a numeric value."
Else
If IsNumeric(i) = True Then
Worksheets.Add(before:=Sheets(1)).Name = "Upload"
WSCount = Worksheets.Count
For i = i + 1 To WSCount
Lrow = Worksheets(i).Find("*", After:=Cells(1, 1), _
LookIn:=xlFormulas, _
Lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Lcol = Worksheets(i).Find("*", After:=Cells(1, 1), _
LookIn:=xlFormulas, _
Lookat:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Pasterow = Lrow + 1
Workbook.Worksheets(i).Range(Cells(1, 1), Cells(Lrow, Lcol)).Copy
Workbook.Worksheets("Upload").Cells(Pasterow, 1).Paste
Next i
Else
Exit Sub
End If
End If
End If
'On Error GoTo 0
'Application.DisplayAlerts = False
End Sub
A common way to find the last row/column is:
With Worksheets(i)
Lrow = .Cells(.Rows.Count, 1).End(xlUp).Row
Lcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
hth
Based on the comment that:
I can't assume any one column or row has the last piece of data because of the variety of the files received.
You should look at using the UsedRange property of the Worksheet (MSDN). UsedRange expands as more data is entered onto the worksheet.
Some people will avoid using UsedRange because if some data has been entered, and then deleted then UsedRange will include these 'empty' cells. The UsedRange will update itself when the workbook is saved. However, in your case, it doesn't sound like this is a relevant issue.
An example would be:
Sub Test()
Dim wsSource As Worksheet
Dim wsTarget As Worksheet
Dim rngSource As Range
Dim rngTarget As Range
Set wsSource = ThisWorkbook.Worksheets("Sheet1")
Set wsTarget = ThisWorkbook.Worksheets("Sheet2")
Set rngSource = wsSource.UsedRange
rngSource.Copy Destination:=wsTarget.Cells
End Sub
Here is a method of finding the last used row and last used column in a worksheet. It avoids the issues with UsedRange and also your issues of not knowing which row might have the last column (and which column might have the last row). Adapt to your purposes:
Option Explicit
Sub LastRowCol()
Dim LastRow As Long, LastCol As Long
With Worksheets("sheet1") 'or any sheet
If Application.WorksheetFunction.CountA(.Cells) > 0 Then
LastRow = .Cells.Find(what:="*", after:=[A1], _
LookIn:=xlFormulas, _
searchorder:=xlByRows, _
searchdirection:=xlPrevious).Row
LastCol = .Cells.Find(what:="*", after:=[A1], _
LookIn:=xlFormulas, _
searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
Debug.Print LastRow, LastCol
End Sub
Although the basic technique has been long used, Siddarth Rout, some time ago, posted a version adding COUNTA to account for the case where the worksheet might be empty -- a useful addition.
If you want to merge data on each sheet into one MasterSheet, run the script below.
Sub CopyRangeFromMultiWorksheets()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'Delete the sheet "RDBMergeSheet" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
'Add a worksheet with the name "RDBMergeSheet"
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"
'loop through all worksheets and copy the data to the DestSh
For Each sh In ActiveWorkbook.Worksheets
If sh.Name <> DestSh.Name Then
'Find the last row with data on the DestSh
Last = LastRow(DestSh)
'Fill in the range that you want to copy
Set CopyRng = sh.Range("A1:G1")
'Test if there enough rows in the DestSh to copy all the data
If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
MsgBox "There are not enough rows in the Destsh"
GoTo ExitTheSub
End If
'This example copies values/formats, if you only want to copy the
'values or want to copy everything look at the example below this macro
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
'Optional: This will copy the sheet name in the H column
DestSh.Cells(Last + 1, "H").Resize(CopyRng.Rows.Count).Value = sh.Name
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
'AutoFit the column width in the DestSh sheet
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Also, see the link below for some other options to do this slightly differently.
http://www.rondebruin.nl/win/s3/win002.htm