Excel VBA Pivot Table Fill Header - vba

In excel vba I am creating a pivot table and want to fill the header (first two rows) and bottom line (1 row) blue with white font.
The below code fills in the interior of the first data set row rather than the header.
With ActiveSheet.PivotTables(1).TableRange1
.Cells.Borders.LineStyle = xlContinuous
.Range("A3:I4").Interior.ColorIndex = 49
.Range("A13:I13").Interior.ColorIndex = 49
End With
Is there a way to dynamically reference the header and the last line of the pivot table? It would be better to reference "pivot table header" than specific ranges incase values of the report change.

you can simply use this:
With ActiveSheet.PivotTables("PivotTable1")
Debug.Print "full headers at: " & Intersect(.Parent.Range(.TableRange1.Row & ":" & .DataBodyRange.Row - 1), .TableRange1).Address
Debug.Print "last row-range at: " & .TableRange1.Rows(1).Offset(.TableRange1.Rows.Count - 1).Address
End With
but looking at your sub, the easiest way would be to change:
.Range("A3:I4").Interior.ColorIndex = 49
.Range("A13:I13").Interior.ColorIndex = 49
to:
Union(.Rows("1:2"), .Rows(.Rows.Count)).Interior.ColorIndex = 49

There is a dynamic way to address the header rows and the last row:
Dim pvtFirstRow, pvtLastRow As Integer
pvtFirstRow = ActiveSheet.PivotTables(1).TableRange1.row + x ' modify x to the first row where your Pivot data starts
pvtLastRow = ActiveSheet.PivotTables(1).TableRange1.Cells(ActiveSheet.PivotTables(1).TableRange1.Cells.count).row

The code presented in the thread so far does work, but will have to be rerun in future: Any time the number of rows or columns in the pivot changes, you may sit with coloured rows or columns where there is no pivot, or coloured cells in the wrong part of the pivot.
I suggest you look at the built in pivot styles of Excel, pick the one closest to what you need, and use code like this to modify the header row(s) and the grand total row. There is no need to manipulate ranges of cells, because next time you might want to turn off the total row (for argument's sake) but the range will still be coloured.
I recorded a macro and removed all redundant lines of code to help you see what changes. I also remove one useful row (.TintAndShade = ) and replaced it with a "ColorIndex = " statement in order to force the shade of blue you seem to prefer.
After running this, you should have the same style available to all other (existing and new) pivots in the same workbook. You could even make it default. No need to run code again to format when your pivot [refreshed] covers a different range of cells.
You can pick another style one from your own palette and use your own custom name for the copy, as long as you find and replace all relevant strings in the sample code below or record your own actions:
Sub ColourPivotStyleForThisBook()
'duplicate blue/medium pivot style from template
ActiveWorkbook.TableStyles("PivotStyleMedium2").Duplicate ("PivotStyleMedium2v2" _
)
With ActiveWorkbook.TableStyles("PivotStyleMedium2v2")
.ShowAsAvailablePivotTableStyle = True
.ShowAsAvailableTableStyle = False
.ShowAsAvailableSlicerStyle = False
.ShowAsAvailableTimelineStyle = False
End With
'now modify header's style (fill) (font colour is white/automatic already)
With ActiveWorkbook.TableStyles("PivotStyleMedium2v2").TableStyleElements( _
xlHeaderRow).Interior
'only one/two lines are changing the colours
' .TintAndShade = -0.249977111117893
.ColorIndex = 49
End With
'now modify grand total row's style (fill) (font colour is white/automatic already)
With ActiveWorkbook.TableStyles("PivotStyleMedium2v2").TableStyleElements( _
xlTotalRow).Interior
'only one/two lines are changing the colours
' .TintAndShade = -0.249946592608417
.ColorIndex = 49
End With
End Sub
If the result you get is not what you're after, simply select part of a pivot in the workbook, tap the Design tab of the PivotTable Tools ribbon, right-click on the one your code has created (it should be in the Custom section at the top), and click Delete. Hope this helps.
(You can even use code like the above to customise PivotTable styles in other workbooks that don't have PivotTables yet.)

Related

Macro to auto fill text in a column down to the last cell in one table (while there are additional tables below)

example of tables in the spreadsheet
Hi I am new to VBA and wanted to create a macro that can auto fill column "Y/N" (as shown in the attached pic) with the text "Yes" by clicking a button.
Right now I have 3 rows in the first table, which is fine and I used the following code:
Sub autofill1()
Range("E12").Value = "Yes"
Range("E12").Select
Selection.AutoFill Destination:=Range("E12:E14")
End Sub
However, the rows in the first table will keep changing, say it becomes 20, 50, 100 depending on different cases. I wonder if there's a way for me to define the auto fill range to be cell E12 to the last cell in this table, not to extend to the rest of the tables below.
I checked the codes provided for other similar questions - looks like it will auto fill all the way down to the last cell in my worksheet, which is the last cell in this column of the 5th table. And I don't want that. Is there a way to only auto fill down to the last cell of the 1st table? Thanks so much in advance!
In my opinion, adding "yes" until found a bottom border.
Sub autofill1()
'Range("E12").Value = "Yes"
'Range("E12").Select
'Selection.AutoFill Destination:=Range("E12:E14")
Dim i As Long
i = 11
Do While Range("E" & i).Borders(xlEdgeBottom).LineStyle _
= xlNone
i = i + 1
Cells(i, 5) = "Yes"
Loop
End Sub

Excel VBA script filters header unexpectedly

Introduction
I have some spreadsheets like the following.
Here the header is on rows 16 and 17. There is a "header" to the left (not shown) among the earlier rows and columns that includes a picture, some non-tabular data, a legend, etc., that is unimportant here. Header text on row 16 is obfuscated because reasons. Data marked in bold red indicates that that sample point has undergone some process. Here is the code snippet from the script that highlights those data points in bold red.
' Traverse columns applying redding until hitting the row end, Comment, or SpGr: whichever comes first
For currIndex = abcDateCol + 1 To lastCol
' Check for exit conditions:
If Cells(abcDateRowDesc, currIndex).Value() = "Comments" Then Exit For
If Cells(abcDateRowDesc, currIndex).Value() <> "" Then
If Cells(abcDateRowDesc, currIndex + 1).Value() = "process" Then
' Looks like we have a column of something Red-able
Columns(ColumnLetter(currIndex) & ":" & ColumnLetter(currIndex + 1)).Select
Selection.AutoFilter ' Turn on autofiltering (hopefully)
Selection.AutoFilter Field:=2, Criteria1:="=1", Operator:=xlOr, Criteria2:="=e"
Selection.Font.ColorIndex = 3
Selection.Font.Bold = True
Selection.AutoFilter ' Turn off autofiltering
Columns(ColumnLetter(currIndex + 1) & ":" & ColumnLetter(currIndex + 1)).EntireColumn.Delete Shift:=xlToLeft
End If
End If
Next currIndex
Context
Here, abcDateCol refers to column AE, lastCol refers to column AQ, abcDateRow (not shown, but available) and abcDateRowDesc refer to the header rows 16 and 17 respectively, and the ColumnLetter function is a user-defined function that returns the human-readable column letter(s) given a column number; this is common functionality you may have seen elsewhere, or even made yourself.
Let's Continue
Never mind that the condition in If Cells(abcDateRowDesc, currIndex).Value() = "Comments" is never satisfied because of an oversight (I'm assuming) -- two different rows, guaranteed.
Let's take a look at what the spreadsheet looks like before this script is executed.
So, the script takes pairs of columns, and for each pair of columns it marks data cells bold red if a data cell's right-adjacent cell has a 1 (or an "e"?) (as a boolean; answers the question, "Has this sample point undergone whatever process?") and then trashes the "process" column.
The Problem
A client wants the gratuitous header gone, so they may more easily import the spreadsheet into whatever solution they have. Delete rows 1 through 15, and this is what I get.
What in the bleepity-bleep happened to the header? I don't understand how this first row gets highlighted. It seems too perfectly weird. Now, let's revisit the very first spreadsheet.
I've filled the "header" with some dummy text after the script executed. Wow, there's the first row reddened again, this time ad infinitum! So, this problem has always existed. Oh, and the first column, too! And, it magically stops right above the proper header so we would never see it.
The Questions
Why is this script unexpectedly reddening the first row and column? Can this be easily solved, or am I looking at some sort of a rewrite? If so, please point me in the general direction.
It helps to mention that these spreadsheets are generated from a Windows application and their scripts executed before a user has a copy of their spreadsheet. Also, regarding the second picture (the spreadsheet with the "process" columns shown), this spreadsheet is not something that normally exists. I generated it for the sake of this post by skipping the script's for loop. The application uses a chosen spreadsheet template, that looks the same minus the data, fills in the sample data, and then executes several scripts over the data.
I considered using conditional formatting, but there are a few dozen spreadsheet templates. Even if I just change the one I need, I can't change the fact that these common scripts run over it. I feel my best option is to correct the script. And, I wouldn't change the script to account for my edge case. The whole ecosystem feels flaky, but that's just subjective.
Note
I am not the author of this script (or any of my company's VBA!). I'm considering this an inheritance tax levied upon me.
*Update
I was asked if I traced through this code. I apologize that I didn't include that information in my original post. Here is what I know. Selection.Font.ColorIndex = 3 turns the cells in the selection that satisfy the autofilter plus the first row (two cells as only two columns are selected at a given time), and Selection.Font.Bold = True makes the same cells bold in the same manner. I suspect it has something to do with the autofilter, so I'm going to take a look at the answers now.
This edit should fix your problems, hopefully (they did for my remake of your spreadsheet, but we won't know til you try on the real thing)
' Traverse columns applying redding until hitting the row end, Comment, or SpGr: whichever comes first
For currIndex = abcDateCol + 1 To lastCol
' Check for exit conditions:
If Cells(abcDateRowDesc, currIndex).Value() = "Comments" Then Exit For
If Cells(abcDateRowDesc, currIndex).Value() <> "" Then
If Cells(abcDateRowDesc, currIndex + 1).Value() = "process" Then
' Looks like we have a column of something Red-able
'Columns(ColumnLetter(currIndex) & ":" & ColumnLetter(currIndex + 1)).Select
With Range(Cells(abcDateRowDesc, currIndex), Cells(abcDateRowDesc, currIndex + 1).End(xlDown))
.AutoFilter 2, "=1", xlOr, "=e"
' Don't format header
With .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count) .Font.ColorIndex = 3
.Font.Bold = True
.AutoFilter ' Turn off autofiltering
End With
End With
Columns(currIndex + 1).Delete xlShiftLeft
End If
End If
Next currIndex
This all starts with a quirk of how the code is choosing its range to autofilter. The selected area is the full column, instead of the area you actually want to format (row 18 to the last entry). It seems that autofiltering on a full column with empty top rows automatically sets the first nonempty row as the header row. So the header is left unfiltered/unhidden by the statement, and it gets colored in as part of the full column selection. So that's why your headers are getting colored.
Now, if you tried to test this by putting data in the above empty rows like "a", those values would become the first ones in the column and would be selected as the headers - meaning those values get colored. Whatever is in the first nonempty row of your columns will be the autofilter header and will get colored.
But that should only affect the columns you explicitly colored, not the entirety of the first row, right? The problem here is that Excel likes to make assumptions about data in order to save time. So if you have a whole row full of red, bold "a"'s and right next to them you put in another "a" to test whether that cell is formatted or not... well, it automatically gives you a red, bold "a" despite the cell being previously unformatted! And if you keep going down the row in this way, it'll appear like your whole row got formatted. But, if you were to jump over a few columns (say, 5-ish) and enter in another "a", voila, it's unformatted, and any "a"s you put in near it will be too. You can also check what Excel by deleting an unformatted "a" in a far off column, then continuing to enter "a"'s all the way down until you reach that same cell - this time, the "a" will be red and bold because all of the others in the row were, too, even though we just checked that this was an unformatted cell!
Basically, having the wrong range for your autofilter made things act very unexpectedly, then trying to test the formatting issue by entering in values just made everything less clear. The code I've provided just autofilters the relevant area (row 17 to the last contiguous row), fixing the core issue.
here's a (commented) refactoring of your code that should do:
Option Explicit
Sub main()
Dim abcDateCol As Long, lastCol As Long, abcDateRow As Long, abcDateRowDesc As Long, currIndex As Long
abcDateCol = 31
lastCol = 43
abcDateRow = 16 '<--| you can change it to 1 for the last "scenario"
abcDateRowDesc = 17 '<--| you can change it to 2 for the last "scenario"
For currIndex = abcDateCol + 1 To lastCol '<--| loop through columns
With Cells(abcDateRow, currIndex) '<--| refer to cell in current column on row abcDateRow
If .Value = "Comments" Then Exit For '<--| Check for exit conditions on row 'abcDateRow'
If .Offset(1).Value <> "" And .Offset(1, 1).Value = "process" Then '<--| Check for processing conditions on row 'abcDateRowDesc'
With .Resize(.Offset(, 1).End(xlDown).Row - .Row + 1, 2) '<-- consider the range from current referenced cell 1 column to the right and down to last 'process' number/letter
.AutoFilter Field:=2, Criteria1:="=1", Operator:=xlOr, Criteria2:="=e" '<--| filter on "process" field with "1" or "e"
If Application.WorksheetFunction.Subtotal(103, .Cells.Resize(, 1)) > 1 Then '<--| if any values match...
With .Offset(2).Resize(.Rows.Count - 2, 1).SpecialCells(xlCellTypeVisible).Font '<--|... consider only filtered values skipping headers (2 rows), and apply formatting
.ColorIndex = 3
.Bold = True
End With
End If
.AutoFilter '<-- reset autofilter
.Resize(, 1).Offset(, 1).EntireColumn.Delete Shift:=xlToLeft '<-- delete the "2nd" column (i.e. one column offsetted to the right)
End With
End If
End With
Next currIndex
End Sub
there were two faults in your "inherited" code:
If Cells(abcDateRowDesc, currIndex).Value() = "Comments" Then Exit For was to be referred to abcDateRow index row instead
the formatting would be applied to all cells, were they filtered (matching) or not

Excel If Cell Is Highlighted on VLOOKUP

I have two sheets in excel, one is an order form the other is a production sheet based off the order form.
I am using VLOOKUP to query the order form for the quantity of a given item ordered.
However, sometimes this quantity is highlighted on the order form, indicating that the item in question actually gets produced 2 extra amounts (for free samples).
So for example, in the production form I have:
ITEM|QUANTITY TO PRODUCE
In the order form I have:
ITEM|QUANTITY TO ORDER
I use VLOOKUP to get the match, and this works, but if a cell in QUANTITY TO ORDER is highlighted yellow, then I need the VLOOKUP value to be added by two.
How can I do this? Is there a way to do this automatically, without a macro? My client doesn't want to be manually activating things, they just expect the sheet to work.
Thank you.
VLOOKUP can't do that. What you need to do, is treat a cell's background color as data... and a cell's background color isn't data.
But... this link explains how to do that and what the implications are.
Create a workbook-scoped name (Ctrl+F3) called BackColor referring to =GET.CELL(63,OFFSET(INDIRECT("RC",FALSE),0,-1)), and then add a column immediately to the right of the column where the user highlights cells, and make that column have a formula such as =BackColor<>0 so that it contains TRUE for any highlighted cell in the column immediately to its left.
Hard-coding the extra 2 units into your formula isn't going to be maintenance-friendly, so enter that 2 in a cell somewhere and define a name called ExtraUnits for it.
Then modify your formula to
=[the original VLOOKUP]+IF([lookup the BackColor Boolean], ExtraUnits, 0)
This will add ExtraUnits to the looked up units, for all highlighted cells.
The only drawback is that, as I said above, a cell's background color isn't data as far as Excel is concerned, so your user must trigger a recalculation - just changing cells' background color will not do that, but pressing F9 will.
The below code was found at http://www.mrexcel.com/forum/excel-questions/215415-formula-check-if-cell-highlighted.html
Function CellColorIndex(InRange As Range, Optional _
OfText As Boolean = False) As Integer
'
' This function returns the ColorIndex value of a the Interior
' (background) of a cell, or, if OfText is true, of the Font in the cell.
'
Application.Volatile True
If OfText = True Then
CellColorIndex = InRange(1,1).Font.ColorIndex
Else
CellColorIndex = InRange(1,1).Interior.ColorIndex
End If
End Function
To use the function:
=IF(CELLCORINDEX(A1,FALSE)>0,1,0)
This lets you check the color of the cell , or the text. But you will need to use the Index-match code found here http://www.mrexcel.com/forum/excel-questions/447723-vlookup-returns-cell-address.html in order to match it up.
Also, like the above answer states, highlighting a cell doesn't count as a data change, so even though you can get this info without a macro, if someone updates the cell's highlight status, it will not update the cells using this formula unless automatically.
Sounds like you may need to rethink the Highlighting being the trigger of the +2 samples. I'm with the above answer that recommends adding a column maybe True/False or Yes/No that is checked to see if they get samples.
What I did is this:
I created a user defined function:
Function getRGB3(rcell As Range, Optional opt As Integer) As Long
Dim C As Long
Dim R As Long
Dim G As Long
Dim B As Long
C = rcell.Interior.Color
R = C Mod 256
G = C \ 256 Mod 256
B = C \ 65536 Mod 256
If opt = 1 Then
getRGB3 = R
ElseIf opt = 2 Then
getRGB3 = G
ElseIf opt = 3 Then
If B <> 0 Then
B = -2
End If
getRGB3 = B + 2
Else
getRGB3 = C
End If
End Function
This made it so all the highlighted cells (in yellow) got a value of 2 when referred to, so on the order form it goes like ITEM|QUANTITY TO ORDER|CUSTOM FUNCTION VALUE| and the third column (custom function) is 2 for each corresponding yellow cell next to it, if not, it is just zero.
Then I do a second VLOOKUP to add the CUSTOM FUNCTION VALUE to the original, and then I have added two. :)

Highlight cells + adjacent 2 cells if certain text if located

If any cell contains the text "example", this cell + the two cells on the same row to the right of, to be highlighted.
So for instance
B5 contains "example", b5,c5,d5 need to be highlighted orange
b9 contains "example", b9,c9,d9 need to be highlighted orange.
And so forth across the whole sheet. Multiple rows and multiple columns could contain the specific text.
Any assistance, examples appreciated.
Private Sub CommandButton1_Click()
row_number = 4
Do
DoEvents
row_number = row_number + 1
swing_data = Sheet1.Range("B" & row_number)
If InStr(swing_data, "Test") >= 1 Then
With Range("B" & row_number).Offset(, 2).Interior
.Pattern = x1solid
.PatternColorIndex = x1automatic
.Color = 65535
.PatternTintAndShade = 0
End With
End If
Loop Until swing_data = ""
End Sub
This is not highlighting the 2 cells to the right, and if there is a blank cell it's stopping. Also it's only working on one column. Needs to work on columns B, E, N, Q, Z, AC,
Changed this line
<<code>With Range("B" & row_number).Offset(, 2).Interiorcode>
To read <code>With Range("B" & row_number).resize(, 3).Interiorcode>
And it works.
Would the be an easier way to include multiple columns in this...?
Conditional formatting:
As multiple columns may contain your criteria you need a bit more complex formula (conditional formatting - use a formula...): =or(iferror(rc[-1]="example",false),iferror(rc[-2]="example",false),rc="example") - notes: I switch to R1C1 reference style before entering conditional formatting as I find it mite clear there; iferror is necessary to make formula working also in first and second columns.
VBA:
you can use VBA find object to cycle through all instances and change formatting there (I think built-in help and maybe recording some short macros give you enough information to create it)
You can do this with conditional formatting if you want to avoid VBA. Select the cells you want you want to apply the formatting to (say B1:D10), Click conditional formatting -> New Rule... -> Use a formula to determine which cells to format. Use this formula
=EXACT($B1,"example")
The $ in front of the column makes sure only that column is looked at, the row will be independent. You then need to change the formatting to whatever you want. In your case change the fill to orange.

Resize pivot chart when selecting different less/more values

When creating the pivot chart using VBA, I set the size of the chart depending on the number of different values that I have in the chart. With pivot charts you have the option to select only some values of the chart. So for example if I have this chart:
And then I select only 2 I get this:
This is too big and sometimes it can be even bigger. What I would like is to resize it automatically when a user select less so that it automatically become smaller. So I would like it to be something like this:
Is there any way to change the width automatically using VBA?
You may want to take a look at these: resize (mrexcel) & Count number of series (stackoverflow)
In these sources there are some code snippets for getting the number of series in a chart and resizing charts to a fit to a range in the worksheet
Im by no means an expert coder, but with the code and concepts in the above you may be able to do something like this to a given ChartObject:
'get the number of series in a given ChartObject:
numberOfSeries = ChartObject.Chart.SeriesCollection.Count
'you can use this to specify a width in cells/as a range. For the purpose of this example I
'eventually want the chart to cover 1 cell width for each series, + 1.
myChartWidth = numberOfSeries + 1
'Give the chart a name. Smarter people may supply a solution that reference the ChartObject
'directly, but I am not that clever
ChartObject.Name = "Your Chart"
'I will use A1 as reference for setting width, change as you see fit
Sheet1.Shapes("Your Chart").Width = Sheet1.Range(Sheet1.Cells(1, 1), Sheet1.Cells(1, myChartWidth)).Width
'I assume you want to fix the height, adjust to your requirements
Sheet1.Shapes("Your Chart").Height = Sheet1.Range("A1:A10").Height
'If you want to also place the chart top left corner in the in the top left of A1, you can do the following
Sheet1.Shapes("Your Chart").Left = Sheet1.Cells(1, 1).Left
Sheet1.Shapes("Your Chart").Top = Sheet1.Cells(1, 1).Top
You probably want to place and size it differently, but I hope the ideas or the linked sources can get you closer to a solution
Finally I found a solution using the change event. So when I select different values I check how many values I have and then resize my chart depending on the values quantity.
Here is an example of what I used (I have 2 different pivot tables in my sheet)
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo GetOut
If Target.PivotTable.Name = "PivotTable1" Then
Sheets("PerPublication").Shapes(1).Width = (Target.PivotTable.RowRange.Count * 50) + 100
End If
If Target.PivotTable.Name = "PivotTable2" Then
Sheets("PerPublication").Shapes(2).Width = (Target.PivotTable.RowRange.Count * 40) + 40
End If
GetOut:
If Err.Description <> "" Then
Err.Clear
End If
End Sub