EXCEL VBA - formatting : merge, wrap text - vba

I've gotten help on this code from a very nice gentleman on SO. This part of the code basically writes a heading that spans over two columns, merge, and wrap-text. I generate this excel extract for headings of different lenghts. For some reports, if the headings are short the height is a regular cells, for others, its x3 in height, even though the length of the heading does not require that much height. Is there a way for me to specify that I want the ROW to always be...say 3 times the height of regular row HEIGHT. I don't want it to vary from report to report. Regardless of the length of the string, I want it to look the same. Is that something like that possible, if I'm using .merge, and .wraptext=true
Do While Not g_RS3.EOF
With xlSheetInsurance.Cells(xlRow, xlCol)
.Value = g_RS3("ShortLabel")
With .Resize(1, 2)
.WrapText = True
.Merge
End With
.Offset(1, 0).Resize(1, 2) = Array("# Clients", "# Students")
.Offset(2, 0).Resize(1, 2).ClearContents
With .Offset(0, 1)
.Resize(1, 2).Merge
.Value = "TOTAL"
.Offset(1, 0).Resize(1, 2) = Array("# Clients", "# Students")
.Offset(2, 0).Resize(1, 2).Formula = _
"=SUMIFS(" & xlSheetInsurance.Range(.Parent.Cells(xlRow + 2, xlStartCol), .Parent.Cells(xlRow + 2, xlCol + 1)).Address(0, 1) & Chr(44) & _
xlSheetInsurance.Range(.Parent.Cells(xlRow + 1, xlStartCol), .Parent.Cells(xlRow + 1, xlCol + 1)).Address(1, 1) & Chr(44) & _
.Parent.Cells(xlRow + 1, xlCol).Address(1, 0) & Chr(41)
.Offset(2, 0).Resize(1, 2).AutoFill .Offset(2, 0).Resize(7, 2) ' AutoFill formula for all Types
.Offset(2, 0).Resize(7, 2).Borders(xlEdgeRight).LineStyle = xlContinuous
End With
With .Resize(2, 4)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
xlCol = xlCol + 2
g_RS3.MoveNext
Loop

This will get you what you need:
With .Resize(1, 2)
.WrapText = True
.Merge
.RowHeight = 45
End With

Related

Stop Excel from jumping to top after filtering/sorting

I had the simple problem that Excel always jumped to the top after a macro ran automatically. Whenever I did a change in any cell the macro runs. However, after finishing, Excel jumps to the top. I want to stay where I edited the cell. I know there are multiple ways to fix this. My solution was one of the following.
Here my code:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Selec, LastRow, LastCol, r As Integer
Dim rng As Range
Set Selec = Range(Target.Address)
LastCol = Range("XFD1").End(xlToLeft).Column
If Selec.Row > Range("A" & Rows.Count).End(xlUp).Row Then
LastRow = Selec.Row
Cells(Selec.Row, 1).value = Application.WorksheetFunction.Max(Range(Cells(1, 1), Cells(Selec.Row - 1, 1))) + 1
With Cells(Selec.Row, 4).Validation
.Delete
.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Parameters!$A$1:$A$3"
End With
Cells(Selec.Row, 4).value = "Not Started"
Else: LastRow = Range("A" & Rows.Count).End(xlUp).Row
End If
Set rng = Range(Cells(1, 1), Cells(LastRow, LastCol))
If Not Application.Intersect(rng, Selec) Is Nothing Then
For r = 2 To LastRow
Select Case Cells(r, 4)
Case "Completed"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(146, 208, 80)
Case "Not Started"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(255, 255, 255)
Case "In Progress"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(255, 255, 0)
End Select
If IsEmpty(Range(Cells(r, 1), Cells(r, LastCol))) = False Then
With Range("A" & r & "," & "C" & r & "," & "D" & r)
.HorizontalAlignment = xlCenter
End With
With Range("B" & r & "," & "E" & r & "," & "F" & r)
.WrapText = True
.HorizontalAlignment = xlLeft
End With
End If
Next r
End If
'Show Only In Progress and Not Started
Worksheets("Task List").Range("A1").AutoFilter Field:=4, Criteria1:=Array("In Progress", "=", "Not Started"), Operator:=xlFilterValues
'Worksheets("Task List").AutoFilter.Sort.SortFields.Add Key:=Range("C1" & Range("C" & Rows.Count).End(xlUp).Row), SortOn:=xlSortOnValues, Order:=xlAscending
Worksheets("Task List").AutoFilter.Sort.SortFields.Add Key:=Range("C:C"), SortOn:=xlSortOnValues, Order:=xlAscending
With Worksheets("Task List").AutoFilter.Sort
.Header = xlYes
.Apply
End With
//Without one of the following lines Excel jumps to the top, but I want to stay at the end/selected cell. Uncommenting one of the following three lines solves this problem.
'Range(Target.Address).Select
'Selection.Select
'Selection.Activate
End Sub
It seems like that the jumping up is a side effect of filtering or sorting. To prevent Excel to jump up just add the following code at the end:
Target.Select
The following two lines of code work too, but are not recommended:
Selection.Select
or
Selection.Activate
All of them should prevent Excel to jump to the top and go back to current selection.
Try this good sir! This makes a temporary view (called "TempView") at the beginning of the code, then shows that view at the end and then immediately deletes this same view.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Selec, LastRow, LastCol, r As Integer
Dim rng As Range
ActiveWorkbook.CustomViews.Add ViewName:="TempView", PrintSettings:=True, _
RowColSettings:=True
Set Selec = Range(Target.Address)
LastCol = Range("XFD1").End(xlToLeft).Column
If Selec.Row > Range("A" & Rows.Count).End(xlUp).Row Then
LastRow = Selec.Row
Cells(Selec.Row, 1).Value = Application.WorksheetFunction.Max(Range(Cells(1, 1), Cells(Selec.Row - 1, 1))) + 1
With Cells(Selec.Row, 4).Validation
.Delete
.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Parameters!$A$1:$A$3"
End With
Cells(Selec.Row, 4).Value = "Not Started"
Else: LastRow = Range("A" & Rows.Count).End(xlUp).Row
End If
Set rng = Range(Cells(1, 1), Cells(LastRow, LastCol))
If Not Application.Intersect(rng, Selec) Is Nothing Then
For r = 2 To LastRow
Select Case Cells(r, 4)
Case "Completed"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(146, 208, 80)
Case "Not Started"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(255, 255, 255)
Case "In Progress"
Range(Cells(r, 1), Cells(r, LastCol)).Interior.Color = RGB(255, 255, 0)
End Select
If IsEmpty(Range(Cells(r, 1), Cells(r, LastCol))) = False Then
With Range("A" & r & "," & "C" & r & "," & "D" & r)
.HorizontalAlignment = xlCenter
End With
With Range("B" & r & "," & "E" & r & "," & "F" & r)
.WrapText = True
.HorizontalAlignment = xlLeft
End With
End If
Next r
End If
'Show Only In Progress and Not Started
Worksheets("Task List").Range("A1").AutoFilter Field:=4, Criteria1:=Array("In Progress", "=", "Not Started"), Operator:=xlFilterValues
'Worksheets("Task List").AutoFilter.Sort.SortFields.Add Key:=Range("C1" & Range("C" & Rows.Count).End(xlUp).Row), SortOn:=xlSortOnValues, Order:=xlAscending
Worksheets("Task List").AutoFilter.Sort.SortFields.Add Key:=Range("C:C"), SortOn:=xlSortOnValues, Order:=xlAscending
With Worksheets("Task List").AutoFilter.Sort
.Header = xlYes
.Apply
End With
ActiveWorkbook.CustomViews("TempView").Show
ActiveWorkbook.CustomViews("TempView").Delete
End Sub

Excel report - formulas VBA

I'm having a little difficulty with something on my report that I'm building in VB6. Bascially I'm building a dynamic report where the Headings, and 2 columns (clients, students) get populated from a recordset. As you can see in the picture, at the end of my headings, I added a TOTAL heading with clients and students below. I'm trying to take a total of all clients in each of the columns and have it be summed up under TOTAL, same with students. The number of columns (UCLA, SDU, SCCU) might vary, so I'm trying to make it dynamic.Basically start with total for A, then B, then C, D and NONE. Any ideas?
EDIT: I select the SHORT LABEL from SQL SERVER and populate until g_RS3 is empty
Do While Not g_RS3.EOF
With xlSheet.Cells(xlRow, xlCol)
.Value = g_RS3("ShortLabel")
.Offset(1, 0).Value = " Clients "
.Offset(1, 1).Value = " Students"
With .Offset(1, 0)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Offset(1, 1)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Resize(1, 2)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.Merge
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
xlCol = xlCol + 2
g_RS3.MoveNext
Loop
With xlSheet.Cells(xlRow, xlCol)
.Value = "TOTAL"
.Offset(1, 0).Value = "Clients"
.Offset(1, 1).Value = "Students"
With .Offset(1, 0)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Offset(1, 1)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Resize(1, 2)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.Merge
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
Then I start in xlrow = 4 xlcol = 2 and populate the CLIENT AND STUDENT columns with data. The loops I have are quite long. But the user will only view the extract. What they do with it once its generated is up to them. The application gives them an option of adding a SHORTLABEL, which needs to be displayed in the extract once they generate it.
Either the SUMIF function or SUMIFS function can perform this handily.
In H4 as a standard formula,
=sumifs($b4:$g4, $b$3:$g$3, h$3)
Fill both right and down.
In VBA as,
with worksheets("Sheet1")
.range("H4:I8").formula = "=sumifs($b4:$g4, $b$3:$g$3, h$3)"
'optional revert to values only
'.range("H4:I8") = .range("H4:I8").values
end with
You will have to determine the extents of the clients/students ranges but half of that is done simply knowing where to put the formula (e.g. H4).
VBA
I've removed a lot of the redundancy that your original code used. Given that you are not yet populating data into the client/student columns, I've used a method where a Total column(s) is always written to the right complete with formulas. If there is another row set, that Totals will be overwritten and a new one created to the right.
Dim xlStartCol As Long
xlStartCol = xlCol
Do While Not g_RS3.EOF
With xlSheet.Cells(xlRow, xlCol)
.Resize(1, 2).Merge
.Value = "TEST" 'g_RS3("ShortLabel")
.Offset(1, 0).Resize(1, 2) = Array("Clients", "Students")
.Offset(2, 0).Resize(1, 2).ClearContents
With .Offset(0, 1)
.Resize(1, 2).Merge
.Value = "Total" 'keep writing Total to the right; it will be overwritten if there is another ShortLabel
.Offset(1, 0).Resize(1, 2) = Array("Clients", "Students")
.Offset(2, 0).Resize(1, 2).Formula = _
"=SUMIFS(" & Range(.Parent.Cells(xlRow + 2, xlStartCol), .Parent.Cells(xlRow + 2, xlCol + 1)).Address(0, 1) & Chr(44) & _
Range(.Parent.Cells(xlRow + 1, xlStartCol), .Parent.Cells(xlRow + 1, xlCol + 1)).Address(1, 1) & Chr(44) & _
.Parent.Cells(xlRow + 1, xlCol - 1).Address(1, 0) & Chr(41)
End With
With .Resize(2, 4)
.Font.Bold = True
.VerticalAlignment = xlCenter
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
xlCol = xlCol + 2
g_RS3.MoveNext
Loop
Once you have actually populated the data into each pair of columns and know the extents, simply use the Range.FillDown method to populate the remaining formulas.
  
I would recommend removing portions of recorded code that are not relevant. Recorded code is very verbose and hampers readability. You might also want to look into the For XML method of creating a query in T-SQL. This will expand the columns returned and allow you to use a fields count to determine the extents.

Excel VBA report building

I've been working on creating a dynamic report in MS Excel. I'm working on a legacy VB6 application and I've come across a few issue that I hope ya'll can help me resolve. What I'm doing below, is grabbing data into my recordset g_RS3 - typically this has anywhere from 3 to 20 items, and I use g_RS3 to enter values (headings, and 2 column values under each heading: clients, buyers) into my excel spreadsheet. I'm trying to make an edit to it but I've been struggling with it. This is my code....
Do While Not g_RS3.EOF
With xlSheet.Cells(xlRow, xlCol)
.Value = g_RS3("Label")
.Offset(1, 0).Value = "Clients"
.Offset(1, 1).Value = "Buyers"
With .Offset(1, 0)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Offset(1, 1)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Resize(1, 2)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.Merge
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
xlCol = xlCol + 2
g_RS3.MoveNext
Loop
I am attaching an image that will show what it looks like. At the end of the recordset I'm trying to add another heading that just says TOTAL and has the 2 columns below it. But I'm having a difficult time doing that.
This is a case where it makes sense to extract a stand-alone piece of functionality from your main code: the header block formatting can go into a separate Sub, so you can call it either from within the recordset loop or for a single set of headings
Main code then becomes
'headers from recordset
Do While Not g_RS3.EOF
DoBlock xlsheet.Cells(xlRow, xlCol), g_RS3("Label"), "Clients", "Buyers"
g_RS3.MoveNext
xlCol = xlCol + 2
Loop
'Extra header
DoBlock xlsheet.Cells(xlRow, xlCol), "Total", "Clients", "Buyers"
Extracted code:
EDIT - tidied up
Sub DoBlock(rng As Range, h1, h2, h3)
With rng
.Value = h1
.WrapText = True
.VerticalAlignment = xlCenter
.HorizontalAlignment = xlCenter
.Offset(1, 0).Value = h2
.Offset(1, 1).Value = h3
With .Resize(2, 2)
.Font.Bold = True
.Borders.Weight = xlThin
End With
.Resize(1, 2).Merge
End With
End Sub
I think just adding one more WITH statement to add the TOTAL cells after your loop would do it. xlCol should already be pointing to the next column based on the last part of the loop (xlCol = xlCol + 2), so I believe this should work.
Do While Not g_RS3.EOF
With xlSheet.Cells(xlRow, xlCol)
.Value = g_RS3("Label")
.Offset(1, 0).Value = "Clients"
.Offset(1, 1).Value = "Buyers"
With .Offset(1, 0)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Offset(1, 1)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Resize(1, 2)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.Merge
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With
xlCol = xlCol + 2
g_RS3.MoveNext
Loop
With xlSheet.Cells(xlRow, xlCol)
.Value = "TOTAL"
.Offset(1, 0).Value = "Clients"
.Offset(1, 1).Value = "Buyers"
With .Offset(1, 0)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Offset(1, 1)
.Font.Bold = True
.Borders.Weight = xlThin
End With
With .Resize(1, 2)
.Font.Bold = True
.WrapText = True
.VerticalAlignment = xlCenter
.Merge
.HorizontalAlignment = xlCenter
.Borders.Weight = xlThin
End With
End With

Merging over 2000 Cells using VBA?

I have wrote the following code to merge cells in excel, the data is around 26000 rows, the code is running on core I7 CPU with 8 GB RAM, the problem that it still working since 4 days, the average rows per day is 3000 row!, any one know how to get the result, because its a report that should be delivered since three days!
Sub MergeCellss()
lastRow = Worksheets("A").Range("A65536").End(xlUp).Row
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
For i = 2 To lastRow
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value <> Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and <> +1 " & intUpper)
End If
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value = Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value = Cells(i - 1, 2).Value Then
Application.DisplayAlerts = False
Debug.Print ("<> +1 and = -1:" & i & "LOWER LIMIT")
DoEvents
For x = 1 To 8
Range(Cells(intUpper, x), Cells(i, x)).Merge
Next x
For j = 18 To 26
Range(Cells(intUpper, j), Cells(i, j)).Merge
Next j
Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(i) & ","">0"")"
Range(Cells(intUpper, 14), Cells(i, 14)).Merge
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value <> Cells(i - 1, 2).Value Then
Debug.Print ("One Cells: " & i)
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
Cells(intUpper, 14).Value = Cells(intUpper, 13).Value
DoEvents
End If
Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
the code above will merge the all cells containing repeated data like User Name, Date of Birth, .... into one cell, and leave the training courses and experiences as it is.
I wonder how can I run this code in less than 1 hour.
Here is some rewrite on your code. The two primary differences are the use of If ... ElseIf ... End If and the grouping of the first and fourth conditional operations (the conditions were the same).
Sub Merge_Cells()
Dim lastRow As Long, rw As Long
Dim intUpper As Long, x As Long
Dim vVALs As Variant
appTGGL bTGGL:=False
Debug.Print Timer
With Worksheets("A")
.Cells(1, 1) = Timer
lastRow = .Cells(Rows.Count, 1).End(xlUp).Row
For rw = 2 To lastRow
vVALs = Array(.Cells(rw - 1, 2).Value, .Cells(rw, 2).Value, .Cells(rw + 1, 2).Value)
If vVALs(1) <> vVALs(0) And vVALs(1) <> vVALs(2) Then
'the first and fourth conditions were the same so they are both here
'original first If condition
intUpper = rw
'Debug.Print ("<> -1 and <> +1 " & intUpper)
'original fourth If condition
'Debug.Print ("One Cells: " & rw)
.Range(.Cells(rw, 1), .Cells(rw, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
.Cells(intUpper, 14).Value = .Cells(intUpper, 13).Value
ElseIf vVALs(1) <> vVALs(0) And vVALs(1) = vVALs(2) Then
intUpper = rw
'Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
ElseIf vVALs(1) = vVALs(0) And vVALs(1) <> vVALs(2) Then
'Debug.Print ("<> +1 and = -1:" & rw & "LOWER LIMIT")
For x = 1 To 26
If x < 9 Or x > 17 Then _
.Range(.Cells(intUpper, x), .Cells(rw, x)).Merge
Next x
.Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(rw) & ","">0"")"
.Range(.Cells(intUpper, 14), .Cells(rw, 14)).Merge
.Cells(rw, 1).Resize(1, 26).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
Next rw
.Cells(1, 2) = Timer
End With
Debug.Print Timer
appTGGL
End Sub
Sub appTGGL(Optional bTGGL As Boolean = True)
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
End Sub
I've also read the three primary conditional values into a variant array to reduce repeated worksheet value reads.

How to find the cell next to active cell in excel

I want to create a log file in excel.
I have created a macro that will insert in-time into active cell on ButtonInTime click. Similarly out time in the active cell on ButtonOutTime click...
Now i want to insert todays date on ButtonInTime click in previous cell of active cell
and
calculate Total Log hours & insert it into next active cell of OutTime.
How i can achive this?
Can any one help me out???
I tried to find out the solution, but didnt get the proper one...
Thanks in advance....
I achieved it.. There are some hard codes in this....
Sub ButtonInTime_Click()
Range("A1").End(xlDown).Select
activecell.Offset(1, 0).Select
activecell.Value = Date
activecell.Offset(0, 1).Value = Time()
activecell.Offset(0, 3).Interior.Color = RGB(255, 0, 0)
activecell.Offset(0, 3).Value = "Log Not Closed!!!"
Range("A" & activecell.Row & ":E" & activecell.Row).Borders(xlEdgeTop).LineStyle = xlContinuous
Range("A" & activecell.Row & ":E" & activecell.Row).Borders(xlEdgeRight).LineStyle = xlContinuous
Range("A" & activecell.Row & ":E" & activecell.Row).Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("A" & activecell.Row & ":E" & activecell.Row).Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("B" & activecell.Row).Borders(xlEdgeRight).LineStyle = xlContinuous
Range("C" & activecell.Row).Borders(xlEdgeRight).LineStyle = xlContinuous
Range("D" & activecell.Row).Borders(xlEdgeRight).LineStyle = xlContinuous
Range("E" & activecell.Row).Borders(xlEdgeRight).LineStyle = xlContinuous
End Sub
Sub ButtonOutTime_Click()
Range("C1").End(xlDown).Select
activecell.Offset(1, 0).Select
activecell.Value = Time()
activecell.Offset(0, 1).Value = activecell.Value - activecell.Offset(0, -1).Value
activecell.Offset(0, 1).Interior.Color = RGB(255, 255, 255)
End Sub