Excel VBA高级图表操作

时间:2013-12-02 14:14:29

标签: excel vba excel-vba diagrams

关于使用VBA在Excel中高级操作图表,我遇到了一个非常具体的问题。

我想尝试根据此屏幕截图进行解释。

my diagrams

对于我的理解以及在文学和网络上经过数小时的研究后,没有办法在绘制图表中产生可比较的和恒定的结果,因为有几个属性是你无法操作的,有些命令没有正确执行。我使用VBA将两个图的绘图区域的宽度设置为700(手动生成图表后):

ActiveChart.PlotArea.Width = 700

之后,一个宽度为698.12,一个宽度为712.34(比较绘图区域的右端)。 此外,基于y轴刻度的最大值(80对100),绘图区域的左边界是可变的。这些问题的结果是不一致的情节区域。

为什么这对我如此重要? 好吧,想象一下你有一个excel计算工具用于项目预测等。这是由几个部门使用的e。 G。因此,贵公司的员工应始终保持一致并具有可比性。这些预测的一部分是容量规划,应该在图表中可视化。因为这样的项目有不同的里程碑e。 G。不同的项目阶段,这些图表应显示代表这些阶段的条形图。 使用辅助轴将不起作用,因为无法将其与主要同步以使其看起来很好。总有一个偏移。 因此,计划是在这些图表中绘制形状。为此,在我的理解中,我需要绘图区域和列等的确切位置,以计算这些条形和其他形状的相对位置。

我希望我的解释足以让你了解我的目标。所以我会回答我的问题:

为什么Excel在每种情况下都以不同的方式解释我的命令(绘图区域宽度)? 是否有可能确定/定义绘图区域? 是否存在比msdn和我迄今为止发现的所有其他来源中记录的更多可操作的对象/属性,我如何获得这些?

我期待你的回复。

编辑:

正如RBarryYoung所问,我写了一些代码,以便你可以重现这个问题。当然,这次没有出现不同右边界的问题。但至少问题是y轴的宽度以及绘图区域的可变宽度(例如绘图区域的左边界的可变位置)是可再现的。如果你自己绘制一些线条,你会看到左边框,因此列本身是偏移的。

Sub DrawChart()

Dim wkb As Workbook
Dim wks As Worksheet
Dim chart1 As Chart
Dim chart2 As Chart
Dim table1 As ListObject
Dim table2 As ListObject
Dim r1 As Range
Dim r2 As Range

Set wkb = ThisWorkbook
Set wks = wkb.Sheets(1)

With wks

    .Cells(1, 1).Value = "Date"
    .Range(.Cells(1, 2), .Cells(1, 10)).Formula = "=today()+column()"
    .Cells(2, 1).Value = "Budget"
    .Range(.Cells(2, 2), .Cells(2, 10)).Formula = "=5*column()"

    .Cells(4, 1).Value = "Date"
    .Range(.Cells(4, 2), .Cells(4, 10)).Formula = "=today()+column()"
    .Cells(5, 1).Value = "Budget"
    .Range(.Cells(5, 2), .Cells(5, 10)).Formula = "=20*column()"


    Set table1 = .ListObjects.Add(SourceType:=xlSrcRange, Source:=Range(Cells(1, 1), Cells(2, 10)), xllistobjecthasheaders:=xlYes)
    table1.Name = "table1"
    Set table2 = .ListObjects.Add(SourceType:=xlSrcRange, Source:=Range(Cells(4, 1), Cells(5, 10)), xllistobjecthasheaders:=xlYes)
    table2.Name = "table2"

Set r1 = Range(.Cells(7, 2), .Cells(17, 15))
Set r2 = Range(.Cells(34, 2), .Cells(44, 15))

Set chart1 = .ChartObjects.Add(r1.Left, r1.Top, r1.Width, r1.Height).Chart

With chart1
    .ChartType = xlColumnStacked
    .SetSourceData Source:=Range("table1[#All]"), PlotBy:=xlRows
    .HasLegend = False
    .ChartArea.Height = 320
    .ChartArea.Width = 620
    .PlotArea.Height = 300
    .PlotArea.Width = 600
End With

Set chart2 = .ChartObjects.Add(r2.Left, r2.Top, r2.Width, r2.Height).Chart

With chart2
    .ChartType = xlColumnStacked
    .SetSourceData Source:=Range("table2[#All]"), PlotBy:=xlRows
    .HasLegend = False
    .ChartArea.Height = 320
    .ChartArea.Width = 620
    .PlotArea.Height = 300
    .PlotArea.Width = 600
End With

End With

End Sub

1 个答案:

答案 0 :(得分:1)

如果我理解正确,你想构建像这样的同步图表: Synchronized scatter line charts

基于Scatter折线图类型的同步图表是使用带有C ++ / CLI的OpenXML SDK生成的。但你的案例的方法应该是一样的。

  

此外,根据y轴刻度的最大值(80对100),绘图区域的左边界是可变的。

PloatArea边距由Excel控制。因此,如图所示,由于PlotArea在给定宽度或高度的情况下自动改变大小,因此在图上分配了足够的空间来放置值轴的刻度标签。在此之前,相同的最小/最大值,相对轴位置和刻度位置已在每个图表上同步。标题和图例被禁用以防止意外对齐,如代码示例中所示。

为了在VBA中同步PlotArea的水平放置,请使用InsideLeft和InsideWidth属性,如下所示:

Sub test_synch()
  With ActiveSheet
    synch_plot_areas .ChartObjects(1), .ChartObjects(2)
  End With
End Sub

Sub synch_plot_areas(ByVal ch1 As ChartObject, ByVal ch2 As ChartObject)
  Dim v_min As Double, v_max As Double, v_delta As Double

  'Align left
  v_min = ch1.Left: If v_min > ch2.Left Then v_min = ch2.Left
  ch1.Left = v_min: ch2.Left = v_min

  'Synchronization of external chart object width
  v_min = ch1.Width: If v_min > ch2.Width Then v_min = ch2.Width
  ch1.Width = v_min: ch2.Width = v_min

  'Margins is controlled by Excel
  'Hence .InsideWidth is sychnronized first to minimum to prevent PlotArea's constraints on margins and placement
  v_min = ch1.Chart.PlotArea.InsideWidth: If v_min > ch2.Chart.PlotArea.Width Then v_min = ch2.Chart.PlotArea.InsideWidth
  With ch1.Chart.PlotArea
    v_delta = .InsideWidth - v_min
    .Width = .Width - v_delta
  End With
  With ch2.Chart.PlotArea
    v_delta = .InsideWidth - v_min
    .Width = .Width - v_delta
  End With

  '.Left is sychnronized second by maximum margin: now there is enough space for Value axis on both charts
  v_max = ch1.Chart.PlotArea.InsideLeft: If v_max < ch2.Chart.PlotArea.InsideLeft Then v_max = ch2.Chart.PlotArea.InsideLeft
  With ch1.Chart.PlotArea
    v_delta = v_max - .InsideLeft
    .Left = .Left + v_delta
  End With
  With ch2.Chart.PlotArea
    v_delta = v_max - .InsideLeft
    .Left = .Left + v_delta
  End With

End Sub

在PlotArea同步之前: synch_PlotArea_before

使用test_synch宏进行PlotArea同步后: synch_PlotArea_after