Excel VBA report building - vba

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

Related

Inconstant column widths when Excel document is sent between computers

An agency I am working with just recently upgraded to Windows 10 and also upgraded from Office 2010 to 2016. All machines have the same image This agency exports reports from SSRS into an Excel document. We have an in house macro that automatically edits and formats the report for publication. For some reason when the same excel document with the same report and formatted table is opened on a different computer, the column widths very. The column widths are set via hard coded values in the macro. This affects the presentation of their final publication and, although it is just barely off, it is unacceptable to their clients.
I have explored a few different possibilities. I first looked at the font and its size after the document was opened on the different machine. They are both the same. I then looked to see if resolution may play a role. Although changing the DPI settings appeared to change the column width values, the DPI settings are the same on all the machines. If the macro is run again on the same report the column widths turn out correctly.
I'm really not sure what else to check. Could someone give me some advice or point me in the right direction?
Sub FormatAppDeptLvl()
'Macro to format Long Bill Department Level report
Dim wsSheet As Worksheet
Dim lastRow As Integer
Dim lastCol As Integer
Dim i As Integer, j As Integer, k As Integer, m As Integer, n As Integer, p As Integer, q As Integer
Dim checktotal As String
'Sheets.Add
Application.DisplayAlerts = False
On Error Resume Next
Set wsSheet = Sheets("NewReport")
On Error GoTo 0
If Not wsSheet Is Nothing Then
'Sheet1 exists
Sheets("NewReport").Delete
Sheets.Add().Name = "NewReport"
Else
'Sheet1 does not exist
Sheets.Add().Name = "NewReport"
End If
Sheets("CLMAppropDept").Select
With ActiveSheet
lastRow = .Cells(.Rows.Count, "F").End(xlUp).Row
End With
With ActiveSheet
lastCol = .Cells(lastRow, .Columns.Count).End(xlToLeft).Column
End With
Range(Cells(1, 1), Cells(lastRow, lastCol)).Select
Selection.Copy
'To unHighlight cells
Cells(1, 1).Select
Sheets("NewReport").Select
Range("A1").Select
ActiveSheet.Paste
ActiveSheet.Unprotect
Range(Cells(1, 1), Cells(lastRow, lastCol)).Select
Selection.Locked = False
Selection.FormulaHidden = True
Call ColorTabs
'Calculate last row and column
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
lastCol = ActiveSheet.Cells(lastRow, ActiveSheet.Columns.Count).End(xlToLeft).Column
'Merge Title
Range(Cells(1, 1), Cells(1, lastCol)).Select
Selection.Merge
'Range("B1:G1").Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = True
End With
'Table dimensions : Rows Height
ActiveSheet.Rows.RowHeight = 10
Range(Cells(1, 1), Cells(1, lastCol)).RowHeight = 12
Range(Cells(2, 1), Cells(2, lastCol)).RowHeight = 18
Range(Cells(3, 1), Cells(lastRow, 1)).RowHeight = 10
'Table dimensions : Column Width
ActiveSheet.Columns.ColumnWidth = 11.86
Range("A:A").ColumnWidth = 29.075
'Table dimensions : Column Width
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
For k = 2 To lastCol
checkfund = Cells(2, k).Value
If checkfund Like "TOTAL*" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 11 'Total
End If
If checkfund Like "GENERAL*" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 12.071432 'GF
End If
If checkfund Like "CASH*" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 12.0714275 'CF
End If
If checkfund Like "REAPPROPRIATED*" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 12.28422 'RF
End If
If checkfund Like "FEDERAL*" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 12.071425 'FF
End If
If checkfund Like "*FTE" Then
Range(Cells(3, k), Cells(lastRow, k)).ColumnWidth = 7.425 'FTE
End If
Next k
'Justify 1st column Left and Bottom
Range(Cells(4, 1), Cells(lastRow, 1)).Select
With Selection
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlCenter 'Bottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = False
End With
'Justify right columns Rigth and Bottom
Range(Cells(4, 2), Cells(lastRow, lastCol)).Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlCenter 'Bottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = False
End With
'Padding
For j = 4 To lastRow
'MsgBox "Cell Len =" & Len(Cells(j, 1).Value)
If Len(Cells(j, 1).Value) > 41 And Len(Cells(j, 1).Value) <= 72 Then
Range(Cells(j, 1), Cells(j, 1)).Select
Range(Cells(j, 1), Cells(j, 1)).RowHeight = 20 '25
With Selection
.WrapText = True
End With
Range(Cells(j, 2), Cells(j, lastCol)).Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlTop
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = False
End With
ElseIf Len(Cells(j, 1).Value) > 72 And Len(Cells(j, 1).Value) <= 108 Then
Range(Cells(j, 1), Cells(j, 1)).Select
Range(Cells(j, 1), Cells(j, 1)).RowHeight = 30 '36
With Selection
.WrapText = True
End With
Range(Cells(j, 2), Cells(j, lastCol)).Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlTop
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = False
End With
ElseIf Len(Cells(j, 1).Value) >= 109 Then
Range(Cells(j, 1), Cells(j, 1)).Select
Range(Cells(j, 1), Cells(j, 1)).RowHeight = 40 '47
With Selection
.WrapText = True
End With
Range(Cells(j, 2), Cells(j, lastCol)).Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlTop
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = False
End With
End If
Next j
'Merging BREAKDOWN row
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
For i = 3 To lastRow
checktotal = Cells(i, 1).Value
If checktotal Like "BREAKDOWN*" Then
Range(Cells(i, 1), Cells(i, lastCol)).Select
With Selection
.VerticalAlignment = xlCenter
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlLTR
.MergeCells = True
.HorizontalAlignment = xlLeft
.Rows.RowHeight = 15
End With
Selection.Merge
Cells(i, 1).RowHeight = 10
End If
Next i
Cells(lastRow + 2, 1).Select
'Clear the Clipboard
Application.CutCopyMode = False
End Sub
I found the solution. It turns out it was the DPI setting after all. The user of the machine on which we were testing had changed the DPI settings in between the time we had checked the settings and actually tested.
When The DPI settings are changed, Display Settings> Advanced Display Settings> Advanced sizing of text and other items> Set a custom scaling level, the initial widths of excel columns in an excel spreadsheet change. I am not sure if this is a result of upgrading to windows 10, or the upgrade to office '16 (or both). Either way, we just have to make sure this setting is uniform on all machines.
I had a similar issue with column widths being different on different computers, and it turned out that the computer where it looked "wrong" did not have a font installed (Meiryo UI), whereas the computer where I had worked on the file did have this font. Also, the image widths were affected -- any picture I had added from the "right" computer looked too narrow on the other one.

VBA merge cells with loop

I want to merge cells two in columns A and B, for example like bellow, and so as long as I have records, below is my code but does not work and does not merge cells do not know what the problem. thanks
.Range("A5", "A6").Merge
.Range("A7", "A8").Merge
.Range("A9", "A10").Merge
.Range("B5", "B6").Merge
.Range("B7", "B8").Merge
.Range("B9", "B10").Merge
Dim i As Integer
Dim j As Integer
Dim xlMerge As Range
Dim xlMergeJ As Range
For i = 5 To ActiveSheet.Cells(Rows.Count, 5).End(xlUp).Row Step 2
Set xlMerge = Range(Cells(i, 1), Cells(i + 1, 1))
With xlMerge
.Merge
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
Next i
For j = 5 To ActiveSheet.Cells(Rows.Count, 5).End(xlUp).Row Step 2
Set xlMergeJ = Range(Cells(j, 2), Cells(j + 1, 1))
With xlMergeJ
.Merge
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
Next j
maybe you're after this:
Option Explicit
Sub main()
Dim i As Long
With ActiveSheet
For i = 5 To .Cells(Rows.count, 1).End(xlUp).row Step 2
With .Cells(i, 1).Resize(2)
.Merge
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
With .Cells(i, 2).Resize(2)
.Merge
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
Next i
End With
End Sub
or its shorter option:
Sub main()
Dim i As Long
With ActiveSheet
For i = 5 To .Cells(Rows.count, 1).End(xlUp).row Step 2
With .Range(.Cells(i, 1).Resize(2).Address & "," & .Cells(i, 2).Resize(2).Address)
.Merge
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
Next i
End With
End Sub

Looping through a number count in quotes in VBA

I am making a table in VBA, and as you can see in my sample table here, I have certain merges about every 2 rows of cells made dependent on various cells. However, a part of the table that can be static is column C in the dark gray. I simply want it to do a number count starting at 1 and continuing to the end of my table but skipping the red horizontal lines and leaving them blank. So, red would be 1, orange 2, yellow 3, etc. Here is the code I want to make more efficient. I could type out every number, but that just seems so inefficient.
range("C3:C8").Select
ActiveCell.FormulaR1C1 = "1"
With Selection
.MergeCells = True
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlBottom
End With
range("C10:C12").Select
ActiveCell.FormulaR1C1 = "2"
With Selection
.MergeCells = True
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
range("C9:C10").Select
ActiveCell.FormulaR1C1 = "3"
With Selection
.MergeCells = True
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlCenter
End With
I would use that there is text in column D.
Sub add_counter()
'Created by Fredrik Östman www.scoc.se
counter = 1
For i = 7 To ActiveSheet.UsedRange.Rows.Count + ActiveSheet.UsedRange.Row
If Cells(i, 4) <> "" Then
Cells(i, 3) = counter
With Range(Cells(i, 3), Cells(i + 1, 3))
.MergeCells = True
.HorizontalAlignment = xlCenterAcrossSelection
.VerticalAlignment = xlBottom
End With
counter = counter + 1
End If
Next i
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 - formatting : merge, wrap text

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