Как предотвратить превращение сводной диаграммы в обычную диаграмму на листовой копии?
используя Excel 2010, я написал некоторые VBA для копирования выбранных листов из главной книги в клиентскую книгу. Код отлично работает, чтобы скопировать лист данных, который имеет данные и сводные таблицы, связанные с данными, и лист диаграммы с одной или несколькими сводными диаграммами в новую книгу.
проблема в том, что в книге назначения диаграммы больше не являются сводными диаграммами, они являются обычными диаграммами, а их диапазон исходных данных пуст. Исходные данные для Master PivotChart заполняется, но серым цветом, поэтому он не редактируется.
проблема появляется сразу после копирования листа из одной книги в другую (на эту строку: XLMaster.Sheets(SlideRS.Fields(2).Value).Copy After:=XLClinic.Sheets(XLClinic.Sheets.Count)
), хотя я включу код для вызова Subs
. К тому времени, когда он добирается до этих линий, график уже сломан
вопрос состоит из трех частей:
- могу ли я предотвратить преобразование сводной диаграммы в обычную диаграмму в копии? т. е. есть флаг/параметр
.Copy
что я пропустил? - если нет, Могу ли я обновить диаграмму, чтобы снова стать сводной диаграммой, что-то вроде
cchart.Chart.PivotLayout.PivotTable = mchart.Chart.PivotLayout.PivotTable
? - при отсутствии любого из них, каков наилучший способ создать сводную диаграмму с нуля в скопированном листе?
Примечания:
- это стандартные сводные таблицы Excel, я не использую PowerPivot. Я открыт для этого, хотя, если это исправит проблему. только что прочитав немного о PowerPivot, я не думаю, что это поможет мне, но, опять же, я открыт для предложений.
- в ответ Йенс' комментарий, необработанные данные и Pivot таблицы находятся на одном листе, А ось графика на втором листе.
вот код. Он отлично работает для всего за исключением копирование листа с неповрежденной сводной диаграммой сводная диаграмма.
While Not SlideRS.EOF 'loop through all the supporting data sheets for this graph sheet
If SlideRS.Fields(1) <> SlideRS.Fields(2) Then 'the worksheet depends on something else, copy it first
If InStr(1, UsedSlides, SlideRS.Fields(2)) = 0 Then 'if the depended upon slide is not in the list of UsedSlides, then add it
Form_Master.ProcessStatus.Value = "Processing: " & ClinicName & " Slide: " & SlideRS!SlideName & " Worksheet: " & SlideRS.Fields(2).Value
XLMaster.Sheets(SlideRS.Fields(2).Value).Copy After:=XLClinic.Sheets(XLClinic.Sheets.Count)
Set NewSheet = XLClinic.Sheets(XLClinic.Sheets.Count)
UsedSlides = UsedSlides & "," & NewSheet.Name
UpdateCharts XLMaster.Sheets(SlideRS.Fields(2).Value), NewSheet
ProcessDataSheet NewSheet, NewXLName
Set NewSheet = Nothing
End If
End If
SlideRS.MoveNext 'move to the next record of supporting Data sheets
Wend
здесь код UpdateCharts
. Его цель-скопировать цвета из Мастера на рабочий лист клиента, так как Excel, похоже, назначает случайные цвета новым диаграммам
Private Sub UpdateCharts(ByRef Master As Worksheet, ByRef Clinic As Worksheet)
Dim MChart As Excel.ChartObject
Dim CChart As Excel.ChartObject
Dim Ser As Excel.Series
Dim pnt As Excel.point
Dim i As Integer
Dim Color() As Long
Dim ColorWheel As ChartColors
Set ColorWheel = New ChartColors
For Each MChart In Master.ChartObjects
For Each CChart In Clinic.ChartObjects
If CChart.Name = MChart.Name Then
If CChart.Chart.ChartType = xlPie Or _
CChart.Chart.ChartType = xl3DPie Or _
CChart.Chart.ChartType = xl3DPieExploded Or _
CChart.Chart.ChartType = xlPieExploded Or _
CChart.Chart.ChartType = xlPieOfPie Then
If InStr(1, CChart.Name, "ColorWheel") Then 'this pie chart needs to have pre-defined colors assigned
i = 1
For Each pnt In CChart.Chart.SeriesCollection(1).Points
pnt.Format.Fill.ForeColor.RGB = ColorWheel.GetRGB("Pie" & i)
i = i + 1
Next
Else 'just copy the colors from XLMaster
'collect the colors for each point in the SINGLE series in the MASTER pie chart
i = 0
For Each Ser In MChart.Chart.SeriesCollection
For Each pnt In Ser.Points
ReDim Preserve Color(i)
Color(i) = pnt.Format.Fill.ForeColor.RGB
i = i + 1
Next 'point
Next 'series
'take that collection of colors and apply them to the CLINIC pie chart points
i = 0
For Each Ser In CChart.Chart.SeriesCollection
For Each pnt In Ser.Points
pnt.Format.Fill.ForeColor.RGB = Color(i)
i = i + 1
Next 'point
Next 'series
End If
Else
'get the series colors from the MASTER
i = 0
For Each Ser In MChart.Chart.SeriesCollection
ReDim Preserve Color(i)
Color(i) = Ser.Interior.Color
i = i + 1
Next 'series
'assign them to the CLINIC
i = 0
For Each Ser In CChart.Chart.SeriesCollection
Ser.Interior.Color = Color(i)
i = i + 1
Next 'series
End If 'pie chart
End If 'clinic chart = master chart
Next 'clinic chart
Next 'master chart
Set ColorWheel = Nothing
End Sub
здесь ProcessDataSheet()
код. Это позволит обновить данные на листе на основе одного или нескольких SQL-запросов, встроенных в лист.
Private Sub ProcessDataSheet(ByRef NewSheet As Excel.Worksheet, ByRef NewXLName As String)
Const InstCountRow As Integer = 1
Const InstCountCol As Integer = 2
Const InstDataCol As Integer = 2
Const InstCol As Integer = 3
Const ClinicNameParm As String = "{ClinicName}"
Const LikeClinicName As String = "{LikeClinicName}"
Const StartDateParm As String = "{StartDate}"
Const EndDateParm As String = "{EndDate}"
Const LocIDParm As String = "{ClinicLoc}"
Dim Data As New ADODB.Recordset
Dim InstCount As Integer
Dim SQLString As String
Dim Inst As Integer
Dim pt As Excel.PivotTable
Dim Rng As Excel.Range
Dim Formula As String
Dim SChar As Integer
Dim EChar As Integer
Dim Bracket As Integer
Dim TabName As String
Dim RowCol() As String
'loop through all the instructions on the page and update the appropriate data tables
InstCount = NewSheet.Cells(InstCountRow, InstCountCol)
For Inst = 1 To InstCount
RowCol = Split(NewSheet.Cells(InstCountRow + Inst, InstDataCol), ",")
SQLString = NewSheet.Cells(InstCountRow + Inst, InstCol)
SQLString = Replace(SQLString, """", "'")
If InStr(1, SQLString, LikeClinicName) > 0 Then
SQLString = Replace(SQLString, LikeClinicName, "'" & ClinicSystoc & "%'")
Else
SQLString = Replace(SQLString, ClinicNameParm, "'" & ClinicSystoc & "'")
End If
SQLString = Replace(SQLString, LocIDParm, "'" & ClinicLocID & "%'")
SQLString = Replace(SQLString, StartDateParm, "#" & StartDate & "#")
SQLString = Replace(SQLString, EndDateParm, "#" & EndDate & "#")
Data.Open Source:=SQLString, ActiveConnection:=CurrentProject.Connection
If Not Data.EOF And Not Data.BOF Then
NewSheet.Cells(CInt(RowCol(0)), CInt(RowCol(1))).CopyFromRecordset Data
End If
Data.Close
Next
'search for all external sheet refrences and truncate them so it points to *this* worksheet
Set Rng = NewSheet.Range(NewSheet.Cells.Address).Find(What:=XLMasterFileName, LookIn:=xlFormulas, Lookat:=xlPart, MatchCase:=False)
While Not Rng Is Nothing
Formula = Rng.Cells(1, 1).Formula
If InStr(1, Formula, "'") > 0 Then
SChar = InStr(1, Formula, "'")
EChar = InStr(SChar + 1, Formula, "'")
Bracket = InStr(1, Formula, "]")
TabName = Mid(Formula, Bracket + 1, EChar - Bracket - 1)
Rng.Replace What:=Mid(Formula, SChar, EChar - SChar + 1), replacement:=TabName, Lookat:=xlPart
End If
Set Rng = NewSheet.Range(NewSheet.Cells.Address).Find(What:=XLMasterFileName, LookIn:=xlFormulas, Lookat:=xlPart, MatchCase:=False)
Wend
Set Rng = Nothing
'fix all the pivot table data sources so they point to *this* spreadsheet
'TODO: add a filter in here to remove blanks
'NOTE: do I want to add for all pivots, or only selected ones?
For Each pt In NewSheet.PivotTables
Formula = pt.PivotCache.SourceData
Bracket = InStr(1, Formula, "!")
Formula = Right(Formula, Len(Formula) - Bracket)
pt.ChangePivotCache XLClinic.PivotCaches.Create(SourceType:=xlDatabase, SourceData:=Formula)
Next
SaveNewXL NewXLName 'yes, save the spreadsheet every single time so that the links in the PPT can be updated to point to it. Sigh...
End Sub
обновление
на основе предложение R3uK, я добавил призыв к началу UpdateCharts
Sub, здесь:
If Master.ChartObjects.Count > 0 Then
Set ColorWheel = New ChartColors 'only do this if we need to
End If
For Each MChart In Master.ChartObjects
If Not MChart.Chart.PivotLayout Is Nothing Then
'Re-copy just the pivot chart from Master to Clinic
CopyPivotChart PivotItemsList, MChart, CChart, Clinic
End If
С CopyPivotChart
здесь:
Private Sub CopydPivotChart(ByVal PivotItemsList As PivotTableItems, ByVal MChart As Excel.ChartObject, ByRef CChart As Excel.ChartObject, ByRef Clinic As Worksheet)
Dim TChart As Excel.ChartObject
'Breakpoint 1
For Each TChart In Clinic.ChartObjects
If TChart.Name = MChart.Name Then
TChart.Delete
End If
Next
MChart.Chart.ChartArea.Copy
'Breakpoint 2
Clinic.PasteSpecial Format:="Microsoft Office Drawing Object", Link:=False, DisplayAsIcon:=False
Clinic.PasteSpecial Format:=2
End Sub
когда я запускаю этот код, я получаю
ошибка выполнения '1004': метод PasteSpecial' объекта'_Worksheet' не
на следующей строке Breakpoint 2
.
теперь, если я пропущу For Each
цикл Breakpoint 1
(вручную перетащите точку выполнения ниже цикла) и вручную удалите диаграмму из рабочего листа Clinic
, тогда код выполняется просто отлично.
4 ответов
1 . в моем скромном опыте копирования сводной диаграммы я не копировал лист, а диаграмму:
Sheets("Graph1").ActiveChart.ChartArea.Copy
ActiveSheet.PasteSpecial Format:="Objet Dessin Microsoft Office", _
Link:=True, DisplayAsIcon:=False
вы пытались создать пустую страницу и вставить в нее диаграмму? Вероятно, вам придется изменить формат, который находится на французском языке, но это должно сделать трюк!
2 . понятия не имею....
3 . для создания сводной таблицы с нуля, у меня нет магических приемов, но я использую это как шаблон :
Sub Create_DCT(ByVal Source_Table_Name As String, ByVal DCT_Sheet_Name As String, ByVal DCT_Name As String)
DeleteAndAddSheet DCT_Sheet_Name
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:=Source_Table_Name, _
Version:=xlPivotTableVersion14). _
CreatePivotTable _
TableDestination:=DCT_Sheet_Name & "!R3C1", _
TableName:=DCT_Name, _
DefaultVersion:=xlPivotTableVersion14
End Sub
Sub Add_Fields_DCT(ByVal DCT_Sheet_Name As String, ByVal DCT_Name As String)
Dim Ws As Worksheet
Set Ws = Worksheets(DCT_Sheet_Name)
'Organized filters
With Ws.PivotTables(DCT_Name).PivotFields("Cluster")
.Orientation = xlPageField
.Position = 1
End With
With Ws.PivotTables(DCT_Name).PivotFields("Region")
.Orientation = xlPageField
.Position = 2
End With
With Ws.PivotTables(DCT_Name).PivotFields("Account")
.Orientation = xlPageField
.Position = 3
'Organized rows
With Ws.PivotTables(DCT_Name).PivotFields("Family")
.Orientation = xlRowField
.Position = 1
End With
With Ws.PivotTables(DCT_Name).PivotFields("Sub_family")
.Orientation = xlRowField
.Position = 2
End With
With Ws.PivotTables(DCT_Name).PivotFields("Invoice_Country")
.Orientation = xlRowField
.Position = 3
End With
With Ws.PivotTables(DCT_Name).PivotFields("Product")
.Orientation = xlRowField
.Position = 4
End With
'Columns : none
' With Ws.PivotTables(DCT_Name).PivotFields("Family")
' .Orientation = xlColumnField
' .Position = 1
' End With
'Data fields (adding, modifying, formatting and compacting)
'Data fiels : Adding
'With Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name)
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Quantity"), "Total Qty", xlSum
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Quantity"), "Avg Qty", xlAverage
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Quantity"), "Qty of Orders", xlCount
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("TotalAmountEUR"), "TO (€)", xlSum
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("UPL"), "Avg UPL", xlAverage
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Discount"), "Avg Discount", xlAverage
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Discount"), "Min Discount", xlMin
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("Discount"), "Max Discount", xlMax
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("PVU"), "Min PVU", xlMin
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("PVU"), "Max PVU", xlMax
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("(PVU-PRI)/PVU"), "Gross Margin", xlAverage
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("(PVU-TC)/PVU"), "Net Margin", xlAverage
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("PVU-PRI"), "Gross Profit (€)", xlSum
Ws.PivotTables(DCT_Name).AddDataField Ws.PivotTables(DCT_Name).PivotFields("PVU-TC"), "Net Profit (€)", xlSum
'End With
'Data fiels : Modifying
' With Ws.PivotTables(DCT_Name).PivotFields("Somme de Quantity")
' .Caption = "Moyenne de Quantity"
' .Function = xlAverage
' End With
'Data formatting
With ActiveSheet.PivotTables(DCT_Name)
.PivotFields("Total Qty").NumberFormat = "# ##0"
.PivotFields("Avg Qty").NumberFormat = "# ##0,#"
.PivotFields("Qty of Orders").NumberFormat = "# ##0"
.PivotFields("TO (€)").NumberFormat = "# ##0 €"
.PivotFields("Avg UPL").NumberFormat = "# ##0 €"
.PivotFields("Avg Discount").NumberFormat = "0,0%"
.PivotFields("Min Discount").NumberFormat = "0,0%"
.PivotFields("Max Discount").NumberFormat = "0,0%"
.PivotFields("Min PVU").NumberFormat = "# ##0 €"
.PivotFields("Max PVU").NumberFormat = "# ##0 €"
.PivotFields("Gross Margin").NumberFormat = "0,0%"
.PivotFields("Net Margin").NumberFormat = "0,0%"
.PivotFields("Gross Profit (€)").NumberFormat = "# ##0 €"
.PivotFields("Net Profit (€)").NumberFormat = "# ##0 €"
End With
'Compact row fields to minimum
For Each PivIt In ActiveSheet.PivotTables(DCT_Name).PivotFields("Sub_family").PivotItems
PivIt.DrillTo "Invoice_Country"
Next PivIt
For Each PivIt In ActiveSheet.PivotTables(DCT_Name).PivotFields("Family").PivotItems
PivIt.DrillTo "Sub_family"
Next PivIt
For Each PivIt In ActiveSheet.PivotTables(DCT_Name).PivotFields("Family").PivotItems
PivIt.DrillTo "Family"
Next PivIt
End Sub
и мой пользовательский Fucntion DeleteAndAddSheet:
Public Function DeleteAndAddSheet(ByVal SheetName As String) As Worksheet
For Each aShe In Sheets
If aShe.Name <> SheetName Then
Else
Application.DisplayAlerts = False
aShe.Delete
Application.DisplayAlerts = True
Exit For
End If
Next aShe
Sheets.Add After:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = SheetName
Set DeleteAndAddSheet = ThisWorkbook.Worksheets(Worksheets.Count)
End Function
надеюсь, что это поможет вам!
Я предлагаю создать вторую книгу, связанную с исходным листом данных, а затем создать соответствующие сводные таблицы во втором листе (оба по существу используют одни и те же данные для заполнения) - я не уверен в следующем пункте, должен ли клиент иметь связанную версию диаграмм или это исключительно для автоматической отчетности?
Если это для автоматической отчетности, то я бы предложил новый подход полностью, используя текущую книгу у вас есть-создать макрос для запуска, когда требуется ontime, ежедневно / еженедельно / ежемесячно и т. д. и отправить диаграммы (из листов, которые вы выбираете только) в формате pdf - у меня есть пример кода для этого, если требуется:)
Я решил проблему, используя .XLTM с поддержкой макросов шаблон действительным шаблон!
вместо открытия файла шаблона затем копирование листов, которые are необходимо из шаблона в новую книгу, теперь я открываю .XLTM, удалите листы, которые не нужен для отчета конкретного клиента. Это полностью устранило необходимость копирования листов, диаграмм и графиков и удалило все ошибки, созданные попытка сделать это.
это специально не решает проблему как чтобы скопировать сводную диаграмму, не теряя ее сводную диаграмму, но она решила проблему большей картины того, как это сделать (я сказал, что открыт для альтернативных предложений).
Если вы копируете лист с данными и лист с диаграммой, связанной с этими данными, скопированная диаграмма не будет ссылаться на данные на скопированном листе, если листы не копируются вместе в одной операции. Похоже, что ваш код сначала копирует лист с сводной таблицей, а затем отдельно копирует лист с диаграммой (лист диаграммы или лист со встроенной диаграммой, не имеет значения). Диаграмма теряет связь с сводной таблицей в исходной книге и становится обычной диаграммой с жестко закодированные значения.
перепишите код, чтобы скопировать оба листа за одну операцию. Затем выполните корректировки сводной таблицы и диаграммы в новой книге.