Situation:
I am using a PivotTableUpdate event to clear a range to the right of a PivotTable and then insert values, in the next available column, to the right of the PivotTable.DataBodyRange.
Single slicer item selection:
In the first image below, I am selecting one slicer item and then using
.Offset(, .Columns.Count).Resize(.Rows.Count, 25)
to clear a region to the right of the DataBodyRange. The Resize is to account for prior selections which may have populated a different column from the currently next available.
I am then using
.Offset(, .Columns.Count).Resize(.Rows.Count, 1) = "1"
to set the next available free column's value to, in this case, 1.
Example: One slicer item selection, table and pivot in Sheet1:
So I am going from "before" to "after" as shown below:
This works fine. There is no change in the next available column address.
Multi-item slicer selection:
When I attempt the same thing with a multi-select the code halts with the standard "Alert before overwriting cells" i.e. I get:
There is already data in..Do you want to replace it?
Example: Multi slicer item selection, table and pivot in Sheet1:
What I have tried:
Application.DisplayAlerts = False
And
Application.AlertBeforeOverwriting = False
And (to clear the pop-up)
Application.SendKeys "{ENTER}"
The message still appears for multi-select and doesn't go away until I manually press the OK
Thoughts:
I had thought perhaps the message is triggered before the event code is run, but I don't get this message when I overwrite the same range (i.e. single slicer item selection)
I know you can manually switch this alert off via the File > Options > Advanced Settings but am unsure how to suppress, via code, in my current code.
Anyone have a solution on how to suppress this alert?
Code:
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
Application.DisplayAlerts = False
With Target.DataBodyRange
Dim clearRange As Range
Set clearRange = .Offset(, .Columns.Count).Resize(.Rows.Count, 25)
clearRange.ClearContents
.Offset(, .Columns.Count).Resize(.Rows.Count, 1) = "1"
End With
Application.DisplayAlerts = True
End Sub
Test data:
| Trust | STP | Val |
|-------|------|-----|
| A | STPa | 1 |
| B | STPb | 2 |
| C | STPc | 3 |
References:
Suppress Overwrite Existing Data Alert In VBA Macro
Prevent Overwrite Warning in VBA
As noted, by the time the event has fired it is too late.
My hack around was to create a public variable holding the pvt.ColumnRange.Columns.Count where the pvt is a hidden duplicate pivottable (of the one in question) that has all the possible selection items in the columns. Then doing a plus 1 on this to set the start area for adding data i.e. no overlap can occur. This was set in an Init procedure so is available before the event is triggered.
Related
I'm working on an Excel financial template that calculates Standard Costs of Manufacturing, and on one tab I need to select from a list (Current, Increase/Decrease, NPI) to tell the calculator to use either a "standard value" or "manual entry", but coming from the same cell.
The Docs saved so that a new WB its set to 'Current' and displays the current 'List Price', 'Bulk Price', and 'Materials Cost' that are called from the 'ROI - Current' to the 'ROI - Target' tab where gross margin is calculated.
However, when you changes through the options, you need to rewrite the called values to whatever it needs to be. When you're doing process improvement, you doing this this a lot just to see how the numbers are moving. So I'm looking for a way to repopulate data a specific way every time a list option is switched.
What I'd like to accomplish is when 'Current' gets selected, the *List Price $(75), Bulk Price $(60), Materials Costs $(18), are called back from the ROI - C tab. (Preferable, nothing would be modifiable unless you changed options, like when using the Validation's error message; but not essential).
Then if Increase/Decrease is selected, List Price and Material Costs populates with the Current $ (but are modifiable fields). Here, Bulk Price is calculated as (CurTabBulk/CurTabList)xIncDecList then used to find the gross margin.
Then when 'NPI' is selected, if only List Price is modified, it calculates bulk price as 0.7 x List Price to assume Material Costs to get gross margin. So as an example of all this:
$75 List x 0.8 = $60 Wholesale X 0.3 = $18 Materials / $60 Wholesale * 100 = 70% Gross Margin (which is the NPI Market Forecast from the ROI - Current Tab building the ROI - Target Tab.)
I can code all this into the cells directly, but whenever you enter your own value, the code that exists in that cell is overwritten. So, I'm looking for a fast 'n dirty way to repopulate it a specific way every time the option is switched.
Thanks all!
UPDATE: So I made it this far at work today using Alt-F11:
I found this code that I put into the VBA:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$G$4" Then
Range("H4:I7").Value = "Select"
End If
End Sub
That will set all validation boxes H4:I7 to "Select" after choosing the Category, but I want each cell to update with the value rather than having to keep selecting them since it's the only value in the list.
I then found this VBA that will reset as I need, but have no idea how to make it work at all...
Sub ResetDropDowns()
Dim rngLists As Range
Dim ListCell As Range
On Error Resume Next
Set rngLists = Sheets("Entry Sheet").UsedRange.SpecialCells(xlCellTypeAllValidation)
On Error GoTo 0
If Not rngLists Is Nothing Then
For Each ListCell In rngLists.Cells
ListCell.Value = Range(Trim(Mid(Replace(ListCell.Validation.Formula1, ":", String(99, " ")), 2, 99))).Value
Next ListCell
End If
End Sub
Also, After the independent Category is chosen, I will need to enter values into the white boxes under Product 1 and 2. These boxes move around depending on what ROI category is selected: Current = No white; Adj/ Price =- List & Costs, NPI = List & GM. So, I'm hoping I can just reassign the code to accomplish calling the 'dependent recalculated Values' to the correct cells as changes are made.
Follow the URL for pictures of the spread sheet. This is the dependent list code that I put into the Validation Source (the result numbers in the image) for Product 1 List Price: =INDEX($E$43:$G$43,,MATCH($G$4,$E$42:$G$42,0))
Thanks all for the help.
https://www.excelforum.com/excel-programming-vba-macros/1387101-auto-populate-dependent-validation-list-with-fist-values-from-list.html
This solves it. Right click the tab to open the Code editor. Copy and past below to the window. Change Sheet1 and the Cells to what ever you need. Create a Macro button using an inserted Icon and right click it and select make macro.
Sub ResetDropDowns()
Dim rngLists As Range
Dim ListCell As Range
On Error Resume Next
Set rngLists = Sheets("Sheet1").Range("G21,H21")
On Error GoTo 0
If Not rngLists Is Nothing Then
For Each ListCell In rngLists.Cells
ListCell.Value = Range(Trim(Mid(Replace(ListCell.Validation.Formula1, ":", String(99, " ")), 2, 99))).Value
Next ListCell
End If
End Sub
Right now I have a combo-box that has two columns in the drop down list. The left column displays a type of a material. The right column is a description of that material. What I would like to do is add headers when I open the drop down list from the combo-box.
I'm pretty sure this is possible because within the properties of the combo-box in the data category there is a property called "columnHeads" which either has a value of "yes" or "no". I toggled this to say "yes" however I don't see any way of changing or adding values to these headers. I assume this must be done from vba, but I was curious if there is a solution to this.
(I am not using a user form by the way which seems to be where the majority of questions about this are being asked)
I have tried specifying where I would like to place my header by providing a position, but this only added the header to the first position in the drop down list not the actual header.
SUB THAT POPULATES THE COMBO-BOX
Here my code is just showing how I populate the array from the SQL database which I use to then populate the combo-Box in excel
Public Sub AFCD_Column_Data(ByVal sql_col As String, _
ByVal sql_table As String)
' ... CODE...
' ... CODE...
' ... MORE CODE!
sqlrs.Open sqlcmd
If Not sqlrs.EOF Then
arr() = sqlrs.GetRows()
End If
Dim i As Integer
Sheet1.ComboBox3.Clear
Sheet1.ComboBox3.ListWidth = 400
Sheet1.ComboBox3.ColumnHeads = True
' Sheet1.ComboBox3.Column(0,0) = "HEADER 1"
For i = 0 To UBound(arr(), 2) ' the 2 refers to the 2nd dimension
With Sheet1.ComboBox3
.AddItem arr(0, i) ' adds the item / value to the first column
.List(i, 1) = arr(0, i) + "(DESCRIPTION)" ' adds the description to the second column
End With
Next i
sqlrs.Close
sqlconxn.Close
Exit Sub
Sorry I don't have an image of my combo-box and the drop down list. I couldn't figure out how to add the image, but all I am looking for is a combo-box where the drop down list displays two headers; followed by the rest of the data which I already have displayed.
Ex:
| "MATERIAL" | "DESCRIPTION" |
--------------------------------
| Titanium | "Description" |
| ... | ... |
I am trying to compare spending data from two sources: a curated manual input from users and an automated extract, for different business units. The common data from both sources is the ID of the spending.
The idea is to aggregate both data sources (excel Tables) into one Table where the first two columns are the ID of the spending, the next column is the spending data from users related to that ID and the last one is the spending data from automated extract.
In this table, I'll have "double" the total spending for each ID, but then I can do a pivot table where I'll clearly compare the users input with the automated extract for each ID.
I highlighted the important fields I need to copy and paste.
[![PGIvsManual][3]][3]
My code is the following
Sub PGIvsManualInput()
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set PGIvsManualTable = Worksheets("PGI vs Dépenses (Auto)").ListObjects("PGIvsManualInputAuto")
Set PGITable = Worksheets("PGI Clean").ListObjects("PGIExtract")
Set ManualInputTable = Worksheets("Dépenses").ListObjects("Dépenses")
'Cleaning the table
With Worksheets("PGI vs Dépenses (Auto)").Range("PGIvsManualInputAuto")
.ClearContents
.Borders(xlInsideHorizontal).LineStyle = xlNone
End With
With PGIvsManualTable
If .ListRows.Count >= 1 Then
.DataBodyRange.Rows.Delete
End If
End With
'Copy the data
PGITable.ListColumns(1).DataBodyRange.Resize(, 2).Copy Destination:= _
PGIvsManualTable
Ant that's where it gets messy. I can't even get the first batch of data to properly import! I am trying to copy the 2 first columns from PGITable and paste them in the 2 first columns of PGIvsManualTable. This worked previously without defining any destination column in my first example, even though both the input and destination Tables didn't have the same number of columns
But in this case, it extends the pasting to all columns of my destination table! I don't understand this comportment as it doesn't happen on my previous example with basically the exact same code!!
I tried to set the destination as follows but always got errors:
PGIvsManualTable.ListColumns(1).DataBodyRange.Resize(, 2) ==> Error 91
PGIvsManualTable.DataBodyRange(1,1) ==> Error 438
PGIvsManualTable.ListColumns(1).Resize(, 2) ==> Error 438
And a few others, but it never worked properly.
I expect the output to be my selected columns copy/pasted properly in my destination column, based on the coordinates I provide in the ListObecjts.DataBodyRange.
I guess that if I manage to make this first import work, all other will work on the same template, but in the meantime, my code seem to work on the previous example.
Deletion of the DataBodyRange.Rows will cause an issue if you then try to paste into the DataBodyRange.
As a workaround, you could delete all rows after the first, something like this example:
Sub Test()
Dim firstTbl As ListObject, secondTbl As ListObject
Set firstTbl = Sheet1.ListObjects("Table1")
Set secondTbl = Sheet1.ListObjects("Table2")
With secondTbl
.DataBodyRange.Clear
If .ListRows.Count > 1 Then
.DataBodyRange.Offset(1).Resize(.ListRows.Count - 1).Rows.Delete
End If
End With
firstTbl.ListColumns(1).DataBodyRange.Resize(, 2).Copy secondTbl.DataBodyRange(1, 1)
End Sub
I am facing a challenge. In a chart with multiple lines, I would like to able when I click on a line or mouse over a line to see the corresponding datapoint in the the table or pivot table...So basically, filtering a table based on the element i click or select on a chart with my mouse.
Do you think that it is achievable ? What would be the methodology ? Is there a VBA code for this ? I have seen examples, but they are working on the oppiste way; click or mouse over an observation and the line is highlighted...
Thanks in advance
saskap
This is really complicated, you have to customize the code for each chart, this example code can be a starting point:
Dim p As Series
Dim pc As Long
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim ch As ChartObject: Set ch = Me.ChartObjects("Chart 1")
With ch
Dim s As Series: Set s = Nothing
On Error Resume Next: Set s = .Chart.SeriesCollection(Target.Value): On Error GoTo 0
If Not p Is Nothing And Not p Is s Then
p.Format.Fill.ForeColor.RGB = pc
End If
If Not s Is Nothing Then
Set p = s
pc = s.Format.Fill.ForeColor.RGB
With s
s.Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
End With
End If
End With
End Sub
The first two rows are global variables that store which Series
object was highlighted last time and what was its original color,
this is needed to restore the original color when a different cell is
selected. Unfortunately global variables in VBA loose their value when the project is reset (e.g. the Stop button is pressed or an error occurs), so it is possible that this code colors a bar and then cannot color it back. If important, these information may be stored in invisible Cells or Chart data but that complicates the code.
The next line means that this is an event handler, a function that is called in response to a certain event, in this case when the selection changes on a certain worksheet (the one on which you insert this - you have to insert this into a worksheet module not a standard code module).
Next we look up the chart based on its name and assign it to a variable, which is of type ChartObject, so early binding will allows us to depend on the support of intellisense (if you type a . it shows members of interfaces).
Then we look up the Series inside the chart based on the name found in the newly selected cell's contents. Since we don't know if the new cell will have a valid name, we have to protect this line with disabling error handling and checking later if the s has a non-nothing value. - This part depends largly on the type of chart and how it represents data, it is possible that you will have to select data based on Series::XValues.
We check if there is a saved value for a previously highlighted bar and if it is different from the current selection, restore its original color.
Finally if the looking up the Series earlier was successful then s is non-null and we save the color of the current bar and highlight it with red fill.
I am trying to create a menu with list boxes in order to be able to select a number of customers from a list in an excel sheet. There are two list boxes, one with all the (default) data and one with the selected customers.
There is no problem adding one customer but when I add a second customer the graphic interface shows nothing, but after some debugging, the SelectedCustomers.RowSource still have the two rows in its data:
?SelectedCustomers.RowSource
$8:$8,$11:$11
This would have me believe there is some error with how I save the data or some limitations to Excel that I am not aware of. This is the code I use to save the data:
Private Sub CommandButton5_Click()
Dim temp As Range
For i = 0 To DefCustomers.ListCount - 1
If DefCustomers.Selected(i) = True Then
If temp Is Nothing Then
Set temp = Range(Rows(i + 4).Address)
Else
Set temp = Application.Union(temp, Range(Rows(i + 4).Address))
End If
End If
Next i
SelectedCustomers.RowSource = temp.Address
End Sub
Has someone experienced this before or know what the problem might be?
Instead of RowSource use AddItem method:
For i = 0 To DefCustomers.ListCount - 1
If DefCustomers.Selected(i) Then
SelectedCustomers.AddItem DefCustomers.Selected(i)
End If
Next i
There are known issues with ListBox.RowSource property in Excel VBA.
[EDIT]
After the discussion...
No matter of number of columns, you can copy rows from source sheet into another sheet, then bind SelectedCustomers listbox to that data