如何获取图表的哪一部分被选中?
How to obtain which part of chart is selected?
我有一些 vsto add-in 到 PowerPoint。
我需要知道用户选择了图表的哪一部分(系列、标题、图表区域、绘图区域、图例等)。有没有可能得到这样的信息?
我当然知道如何获取所选图表。
PowerPoint 对象模型不提供任何 属性 或方法。
我的加载项是用 VBA 编写的,但我认为以下内容会对您有所帮助。 PPT 对象模型不支持这一点,所以我的 hacky 解决方案是将删除线字体应用为 ExecuteMSO 命令(即删除线应用于所选的任何内容),然后我遍历图表的每个元素并寻找删除线。当我们找到它时,我们可以知道用户选择了什么,应用我们想要的任何规则,并删除删除线。
就我而言,我想重写 Bold 命令,以便我们可以对用户的选择应用不同的字体粗细,而不是使用本机的仿粗体。这是我的解决方案的一部分:
首先,这是当所选内容包含形状时调用的子程序。请注意我们如何处理图表场景:
Private Sub commandBoldSelectedShapes(mySelection As Selection)
Debug.Print "IN_commandBoldSelectedShapes"
Dim oShp As Shape
Dim oSmrtArt As SmartArt
Dim oTable As Table
Dim oChart As Chart
Dim oCell As Cell
Dim i As Long
Dim j As Long
Dim ctr As Long
Dim oFont As Font
For ctr = 1 To mySelection.ShapeRange.Count
Set oShp = mySelection.ShapeRange(ctr)
If oShp.Type = msoGroup Then
RefontTypoGroup oShp, mySelection
ElseIf oShp.HasSmartArt Then
Set oSmrtArt = oShp.SmartArt
DoEvents
Application.CommandBars.ExecuteMso ("Strikethrough")
DoEvents
RefontTypoSmartArt oSmrtArt
ElseIf oShp.HasTable Then
Debug.Print "Seeing a table!"
Set oTable = oShp.Table
If ctr = 1 And mySelection.ShapeRange.Count = 1 Then
With oTable
For i = 1 To oTable.Rows.Count
For j = 1 To oTable.Columns.Count
Set oCell = oTable.Rows(i).Cells(j)
If oCell.Selected Then
Set oFont = oCell.Shape.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
End If
Next
Next
End With
Else
For i = 1 To oTable.Rows.Count
For j = 1 To oTable.Columns.Count
Set oCell = oTable.Rows(i).Cells(j)
Set oFont = oCell.Shape.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
Next
Next
End If
' Charts are highly problematic because the VBA Selection object
' doesn't allow you to figure out which element(s) in a chart the user
' may have selected. You can only see that the full shape containing a chart
' has been selected. So my solution was to run an
' ExecuteMso - Strikethrough command. Then, separate macros
' go through the whole chart looking for strikethoughs and replace them
' with bolded/unbolded text and the correct font weight.
ElseIf oShp.HasChart Then
Debug.Print "Seeing a chart!"
Set oChart = oShp.Chart
If ctr = 1 And mySelection.ShapeRange.Count = 1 Then
DoEvents
Application.CommandBars.ExecuteMso ("Strikethrough")
DoEvents
RefontTypoChart oChart
Exit Sub
' If there is more than one shape selected, including a chart,
' and that chart is not the first shape selected, we know that
' the whole chart has been selected. As a result, we can simply
' apply bolding to the whole chart.
Else
With oChart.ChartArea.Format.TextFrame2.TextRange.Font
If GlobalSettings.IsBoldPressed = False Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
Else
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End With
End If
ElseIf oShp.HasTextFrame Then
If oShp.TextFrame.HasText Then
Set oFont = oShp.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
End If
End If
Next
End Sub
还有一个子程序开始遍历图表元素。大多数检查都将删除线搜索外包给另一个子:
Sub RefontTypoChart(chrt As Chart)
On Error GoTo Errhandler
' Dim s As Series
Dim A As axis
' Dim scnt As Integer
Dim i As Integer
Dim oShp As Shape
Dim oTxtRange2 As TextRange2
Dim oTickLabels As TickLabels
Dim oLegendEntries As LegendEntries
Set oTxtRange2 = chrt.Format.TextFrame2.TextRange
If oTxtRange2.Font.Strikethrough = msoTrue Then
RefontTypoChartShapeRange oTxtRange2
Exit Sub
End If
If chrt.HasLegend Then
Set oLegendEntries = chrt.Legend.LegendEntries
For i = 1 To oLegendEntries.Count
With oLegendEntries(i).Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
.Strikethrough = False
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
.Strikethrough = False
End If
End If
End With
Next
With chrt.Legend.Format.TextFrame2.TextRange.Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
.Strikethrough = False
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
.Strikethrough = False
End If
End If
End With
End If
If chrt.HasTitle Then
Set oTxtRange2 = chrt.ChartTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
If chrt.HasAxis(xlCategory, xlPrimary) Then
Set A = chrt.Axes(xlCategory, xlPrimary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlCategory, xlSecondary) Then
Set A = chrt.Axes(xlCategory, xlSecondary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlValue, xlPrimary) Then
Set A = chrt.Axes(xlValue, xlPrimary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlValue, xlSecondary) Then
Set A = chrt.Axes(xlValue, xlSecondary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
RefontTypoChartLabels chrt
If chrt.Shapes.Count > 0 Then
For Each oShp In chrt.Shapes
If oShp.HasTextFrame Then
If oShp.TextFrame.HasText Then
Set oTxtRange2 = oShp.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
End If
Next
End If
Exit Sub
Errhandler:
Debug.Print "Error: " & Err.Description
End Sub
这是查找大部分删除线的子项:
Public Sub RefontTypoShapeRange(oTxtRange2 As TextRange2)
Dim i As Long
With oTxtRange2
For i = .Runs.Count To 1 Step -1
With .Runs(i).Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End If
End With
Next
.Font.Strikethrough = False
End With
End Sub
您可能会注意到,在发布的第二个子项中,引用了一些专门用于某些图表元素的不同子项。这是因为 TickLabels 没有 TextRange2 对象,因此需要它们自己的检查子(一个传递 TickLabels 对象的)。此外,可以具有多种格式 运行 的图表元素与不能具有多种格式的图表元素之间存在区别——在不支持的图表元素的 TextRange2 对象中查找 运行s超过 1 运行 会导致崩溃。
Public Sub RefontTypoChartShapeRange(oTxtRange2 As TextRange2)
Debug.Print "IN_RefontTypoChartShapeRange"
With oTxtRange2.Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough <> msoFalse Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
End If
Else
If .Strikethrough <> msoFalse Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End If
.Strikethrough = False
End With
End Sub
图表数据标签也是一个小噩梦,因为如果我们不修改 .Autotext 属性,它们将与数据断开连接,如下所示。
Sub RefontTypoChartLabels(oChrt As Chart)
Dim i As Integer
Dim j As Integer
Dim seriesVar As Series
Dim dataLabelsVar As DataLabels
Dim dataLabelVar As DataLabel
Dim pointVar As Point
Dim oTxtRange2 As TextRange2
Dim isAutoText As Boolean
For i = 1 To oChrt.SeriesCollection.Count
Set seriesVar = oChrt.SeriesCollection(i)
If seriesVar.HasDataLabels = True Then
Set dataLabelsVar = seriesVar.DataLabels
If dataLabelsVar.Format.TextFrame2.TextRange.Font.Strikethrough <> msoFalse Then
Set oTxtRange2 = dataLabelsVar.Format.TextFrame2.TextRange
RefontTypoChartShapeRange oTxtRange2
Else
For j = 1 To seriesVar.Points.Count
Set pointVar = seriesVar.Points(j)
If pointVar.HasDataLabel = True Then
Set dataLabelVar = seriesVar.DataLabels(j)
isAutoText = dataLabelVar.AutoText
Set oTxtRange2 = dataLabelVar.Format.TextFrame2.TextRange
RefontTypoChartShapeRange oTxtRange2
dataLabelVar.AutoText = isAutoText
End If
Next
End If
End If
Next
End Sub
希望您能够根据自己的需要调整其中的一些内容,避免拔头发。如果您认为某个地方的某个人可能需要在图表中使用删除线字体,您也可以使用阴影而不是删除线。
我有一些 vsto add-in 到 PowerPoint。
我需要知道用户选择了图表的哪一部分(系列、标题、图表区域、绘图区域、图例等)。有没有可能得到这样的信息?
我当然知道如何获取所选图表。
PowerPoint 对象模型不提供任何 属性 或方法。
我的加载项是用 VBA 编写的,但我认为以下内容会对您有所帮助。 PPT 对象模型不支持这一点,所以我的 hacky 解决方案是将删除线字体应用为 ExecuteMSO 命令(即删除线应用于所选的任何内容),然后我遍历图表的每个元素并寻找删除线。当我们找到它时,我们可以知道用户选择了什么,应用我们想要的任何规则,并删除删除线。
就我而言,我想重写 Bold 命令,以便我们可以对用户的选择应用不同的字体粗细,而不是使用本机的仿粗体。这是我的解决方案的一部分:
首先,这是当所选内容包含形状时调用的子程序。请注意我们如何处理图表场景:
Private Sub commandBoldSelectedShapes(mySelection As Selection)
Debug.Print "IN_commandBoldSelectedShapes"
Dim oShp As Shape
Dim oSmrtArt As SmartArt
Dim oTable As Table
Dim oChart As Chart
Dim oCell As Cell
Dim i As Long
Dim j As Long
Dim ctr As Long
Dim oFont As Font
For ctr = 1 To mySelection.ShapeRange.Count
Set oShp = mySelection.ShapeRange(ctr)
If oShp.Type = msoGroup Then
RefontTypoGroup oShp, mySelection
ElseIf oShp.HasSmartArt Then
Set oSmrtArt = oShp.SmartArt
DoEvents
Application.CommandBars.ExecuteMso ("Strikethrough")
DoEvents
RefontTypoSmartArt oSmrtArt
ElseIf oShp.HasTable Then
Debug.Print "Seeing a table!"
Set oTable = oShp.Table
If ctr = 1 And mySelection.ShapeRange.Count = 1 Then
With oTable
For i = 1 To oTable.Rows.Count
For j = 1 To oTable.Columns.Count
Set oCell = oTable.Rows(i).Cells(j)
If oCell.Selected Then
Set oFont = oCell.Shape.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
End If
Next
Next
End With
Else
For i = 1 To oTable.Rows.Count
For j = 1 To oTable.Columns.Count
Set oCell = oTable.Rows(i).Cells(j)
Set oFont = oCell.Shape.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
Next
Next
End If
' Charts are highly problematic because the VBA Selection object
' doesn't allow you to figure out which element(s) in a chart the user
' may have selected. You can only see that the full shape containing a chart
' has been selected. So my solution was to run an
' ExecuteMso - Strikethrough command. Then, separate macros
' go through the whole chart looking for strikethoughs and replace them
' with bolded/unbolded text and the correct font weight.
ElseIf oShp.HasChart Then
Debug.Print "Seeing a chart!"
Set oChart = oShp.Chart
If ctr = 1 And mySelection.ShapeRange.Count = 1 Then
DoEvents
Application.CommandBars.ExecuteMso ("Strikethrough")
DoEvents
RefontTypoChart oChart
Exit Sub
' If there is more than one shape selected, including a chart,
' and that chart is not the first shape selected, we know that
' the whole chart has been selected. As a result, we can simply
' apply bolding to the whole chart.
Else
With oChart.ChartArea.Format.TextFrame2.TextRange.Font
If GlobalSettings.IsBoldPressed = False Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
Else
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End With
End If
ElseIf oShp.HasTextFrame Then
If oShp.TextFrame.HasText Then
Set oFont = oShp.TextFrame.TextRange.Font
checkBoldsNoStrikethrough oFont
End If
End If
Next
End Sub
还有一个子程序开始遍历图表元素。大多数检查都将删除线搜索外包给另一个子:
Sub RefontTypoChart(chrt As Chart)
On Error GoTo Errhandler
' Dim s As Series
Dim A As axis
' Dim scnt As Integer
Dim i As Integer
Dim oShp As Shape
Dim oTxtRange2 As TextRange2
Dim oTickLabels As TickLabels
Dim oLegendEntries As LegendEntries
Set oTxtRange2 = chrt.Format.TextFrame2.TextRange
If oTxtRange2.Font.Strikethrough = msoTrue Then
RefontTypoChartShapeRange oTxtRange2
Exit Sub
End If
If chrt.HasLegend Then
Set oLegendEntries = chrt.Legend.LegendEntries
For i = 1 To oLegendEntries.Count
With oLegendEntries(i).Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
.Strikethrough = False
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
.Strikethrough = False
End If
End If
End With
Next
With chrt.Legend.Format.TextFrame2.TextRange.Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
.Strikethrough = False
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
.Strikethrough = False
End If
End If
End With
End If
If chrt.HasTitle Then
Set oTxtRange2 = chrt.ChartTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
If chrt.HasAxis(xlCategory, xlPrimary) Then
Set A = chrt.Axes(xlCategory, xlPrimary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlCategory, xlSecondary) Then
Set A = chrt.Axes(xlCategory, xlSecondary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlValue, xlPrimary) Then
Set A = chrt.Axes(xlValue, xlPrimary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
If chrt.HasAxis(xlValue, xlSecondary) Then
Set A = chrt.Axes(xlValue, xlSecondary)
If A.HasTitle = True Then
Set oTxtRange2 = A.AxisTitle.Format.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
Set oTickLabels = A.TickLabels
RefontTypoTickLabels oTickLabels
End If
RefontTypoChartLabels chrt
If chrt.Shapes.Count > 0 Then
For Each oShp In chrt.Shapes
If oShp.HasTextFrame Then
If oShp.TextFrame.HasText Then
Set oTxtRange2 = oShp.TextFrame2.TextRange
RefontTypoShapeRange oTxtRange2
End If
End If
Next
End If
Exit Sub
Errhandler:
Debug.Print "Error: " & Err.Description
End Sub
这是查找大部分删除线的子项:
Public Sub RefontTypoShapeRange(oTxtRange2 As TextRange2)
Dim i As Long
With oTxtRange2
For i = .Runs.Count To 1 Step -1
With .Runs(i).Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough = True Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
End If
Else
If .Strikethrough = True Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End If
End With
Next
.Font.Strikethrough = False
End With
End Sub
您可能会注意到,在发布的第二个子项中,引用了一些专门用于某些图表元素的不同子项。这是因为 TickLabels 没有 TextRange2 对象,因此需要它们自己的检查子(一个传递 TickLabels 对象的)。此外,可以具有多种格式 运行 的图表元素与不能具有多种格式的图表元素之间存在区别——在不支持的图表元素的 TextRange2 对象中查找 运行s超过 1 运行 会导致崩溃。
Public Sub RefontTypoChartShapeRange(oTxtRange2 As TextRange2)
Debug.Print "IN_RefontTypoChartShapeRange"
With oTxtRange2.Font
If GlobalSettings.IsBoldPressed = False Then
If .Strikethrough <> msoFalse Then
.Bold = False
.Name = FontsSettings.ActiveFonts.bodyFont
End If
Else
If .Strikethrough <> msoFalse Then
.Bold = True
.Name = FontsSettings.ActiveFonts.headingFont
End If
End If
.Strikethrough = False
End With
End Sub
图表数据标签也是一个小噩梦,因为如果我们不修改 .Autotext 属性,它们将与数据断开连接,如下所示。
Sub RefontTypoChartLabels(oChrt As Chart)
Dim i As Integer
Dim j As Integer
Dim seriesVar As Series
Dim dataLabelsVar As DataLabels
Dim dataLabelVar As DataLabel
Dim pointVar As Point
Dim oTxtRange2 As TextRange2
Dim isAutoText As Boolean
For i = 1 To oChrt.SeriesCollection.Count
Set seriesVar = oChrt.SeriesCollection(i)
If seriesVar.HasDataLabels = True Then
Set dataLabelsVar = seriesVar.DataLabels
If dataLabelsVar.Format.TextFrame2.TextRange.Font.Strikethrough <> msoFalse Then
Set oTxtRange2 = dataLabelsVar.Format.TextFrame2.TextRange
RefontTypoChartShapeRange oTxtRange2
Else
For j = 1 To seriesVar.Points.Count
Set pointVar = seriesVar.Points(j)
If pointVar.HasDataLabel = True Then
Set dataLabelVar = seriesVar.DataLabels(j)
isAutoText = dataLabelVar.AutoText
Set oTxtRange2 = dataLabelVar.Format.TextFrame2.TextRange
RefontTypoChartShapeRange oTxtRange2
dataLabelVar.AutoText = isAutoText
End If
Next
End If
End If
Next
End Sub
希望您能够根据自己的需要调整其中的一些内容,避免拔头发。如果您认为某个地方的某个人可能需要在图表中使用删除线字体,您也可以使用阴影而不是删除线。