FlexChart > FlexChart の操作 > FlexChart の要素 > 注釈 > 吹き出しの作成 |
チャート内で吹き出しを使用して、データ系列や個別のデータポイントの詳細を読みやすい形式で表示できます。データポイントに接続された吹き出しは、チャート領域内の見た目の乱雑さを最小限に抑え、チャートデータを見やすく、かつわかりやすくします。FlexChart では、Polygon 型の注釈をカスタマイズして、直線または矢印の接続線が付いた吹き出しをチャートに作成できます。
この例では、クイックスタートで作成したサンプルを使用して、矢印付き吹き出しと接続線付きの多角形注釈を作成します。それには、Points プロパティと ContentCenter プロパティを使用して、それぞれ多角形の頂点の座標と注釈のコンテンツの中心を定義します。
それぞれのデータポイントに接続される吹き出しを作成するには、次の手順に従います。
次の図に、矢印と接続線でデータポイントに接続される多角形注釈を示します。
直線付きの吹き出しを作成するには、次のコードを使用します。
... lineCallouts.SeriesIndex = 2 lineCallouts.PointIndex = 2 lineCallouts.ContentCenter = New Point(-50, 75) Dim lineCalloutsPoints As PointCollection = New PointCollection() lineCalloutsPoints.Add(New Point(0, 0)) lineCalloutsPoints.Add(New Point(25, -25)) lineCalloutsPoints.Add(New Point(50, -25)) lineCalloutsPoints.Add(New Point(50, -50)) lineCalloutsPoints.Add(New Point(25, -75)) lineCalloutsPoints.Add(New Point(0, -50)) lineCalloutsPoints.Add(New Point(0, -25)) lineCalloutsPoints.Add(New Point(25, -25)) lineCalloutsPoints.Add(New Point(0, 0)) lineCallouts.Points = lineCalloutsPoints flexChart.Invalidate() End Sub
// 多角形の線吹き出しの注釈を作成する lineCallout.SeriesIndex = 2; lineCallout.PointIndex = 2; lineCallout.ContentCenter = new Point(25, -40); // 線吹き出しの注釈のポイントのリストを作成する var lineConnectorPoints = new PointCollection(); lineConnectorPoints.Add(new Point(0, 0)); lineConnectorPoints.Add(new Point(25, -25)); lineConnectorPoints.Add(new Point(50, -25)); lineConnectorPoints.Add(new Point(50, -50)); lineConnectorPoints.Add(new Point(25, -75)); lineConnectorPoints.Add(new Point(0, -50)); lineConnectorPoints.Add(new Point(0, -25)); lineConnectorPoints.Add(new Point(25, -25)); lineConnectorPoints.Add(new Point(0, 0)); lineCallout.Points = lineConnectorPoints; flexChart.Invalidate(); }
Private Sub SetUpAttotations() ' 多角形の矢印吹き出しの注釈を作成する Dim arrowCalloutContentCenter As Point = New Point(25, -50) arrowCallouts.ContentCenter = arrowCalloutContentCenter ' GetPointsForArrowCallout()を呼び出して矢印吹き出しのポイントのリストを作成する arrowCallouts.Points = GetPointsForArrowCallout(arrowCalloutContentCenter.X, arrowCalloutContentCenter.Y, "Low") arrowCallouts.SeriesIndex = 1 arrowCallouts.PointIndex = 2 ...
private void SetUpAnnotations() { // 多角形の矢印吹き出しの注釈を作成する var arrowCalloutContentCenter = new Point(25, -50); arrowCallout.ContentCenter = arrowCalloutContentCenter; // GetPointsForArrowCallout()を呼び出して矢印吹き出しのポイントのリストを作成する arrowCallout.Points = GetPointsForArrowCallout(arrowCalloutContentCenter.X, arrowCalloutContentCenter.Y, "Low"); arrowCallout.SeriesIndex = 1; arrowCallout.PointIndex = 2;
Private Function GetPointsForArrowCallout(centerX As Double, centerY As Double, content As String) As PointCollection Dim size As _Size = _engine.MeasureString(content) Return GetPointsForArrowCallout(centerX, centerY, size.Width + 10, size.Height + 10) End Function
PointCollection GetPointsForArrowCallout(double centerX, double centerY, string content) { _Size size = _engine.MeasureString(content); return GetPointsForArrowCallout(centerX, centerY, (float)size.Width + 10, (float)size.Height + 10); }
Private Function GetPointsForArrowCallout(centerX As Double, centerY As Double, rectWidth As Double, rectHeight As Double) As PointCollection Dim points As PointCollection = New PointCollection() Dim rectLeft As Double = centerX - rectWidth / 2 Dim rectRight As Double = centerX + rectWidth / 2 Dim rectTop As Double = centerY - rectHeight / 2 Dim rectBottom As Double = centerY + rectHeight / 2 Dim angle As Double = Math.Atan2(-centerY, centerX) Dim angleOffset1 As Double = 0.4 Dim angleOffset2 As Double = 0.04 Dim arrowHeight As Double = 0.4 * rectHeight Dim hypotenuse As Double = arrowHeight / Math.Cos(angleOffset1) Dim subHypotenuse As Double = arrowHeight / Math.Cos(angleOffset2) Dim isNearBottom As Boolean = Math.Abs(rectTop) > Math.Abs(rectBottom) Dim nearHorizontalEdge As Double If (isNearBottom) Then nearHorizontalEdge = rectBottom Else nearHorizontalEdge = rectTop End If Dim isNearRight As Boolean = Math.Abs(rectLeft) > Math.Abs(rectRight) Dim nearVerticalEdge As Double If (isNearRight) Then nearVerticalEdge = rectRight Else nearVerticalEdge = rectLeft End If Dim isHorizontalCrossed As Boolean = Math.Abs(nearHorizontalEdge) > Math.Abs(nearVerticalEdge) Dim nearEdge As Double If (isHorizontalCrossed) Then nearEdge = nearHorizontalEdge Else nearEdge = nearVerticalEdge End If Dim factor As Int16 If (nearEdge > 0) Then factor = -1 Else factor = 1 End If Dim crossedPointOffsetToCenter As Double If (isHorizontalCrossed) Then crossedPointOffsetToCenter = rectHeight / (2 * Math.Tan(angle)) * factor Else crossedPointOffsetToCenter = rectWidth * Math.Tan(angle) * factor / 2 End If '矢印のポイント points.Add(New Point(0, 0)) points.Add(New Point(Math.Cos(angle + angleOffset1) * hypotenuse, -Math.Sin(angle + angleOffset1) * hypotenuse)) points.Add(New Point(Math.Cos(angle + angleOffset2) * subHypotenuse, -Math.Sin(angle + angleOffset2) * subHypotenuse)) '四角形のポイント If (isHorizontalCrossed) Then points.Add(New Point(-nearEdge / Math.Tan(angle + angleOffset2), nearEdge)) If (isNearBottom) Then points.Add(New Point(rectLeft, rectBottom)) points.Add(New Point(rectLeft, rectTop)) points.Add(New Point(rectRight, rectTop)) points.Add(New Point(rectRight, rectBottom)) Else points.Add(New Point(rectRight, rectTop)) points.Add(New Point(rectRight, rectBottom)) points.Add(New Point(rectLeft, rectBottom)) points.Add(New Point(rectLeft, rectTop)) End If points.Add(New Point(-nearEdge / Math.Tan(angle - angleOffset2), nearEdge)) Else points.Add(New Point(nearEdge, -nearEdge * Math.Tan(angle + angleOffset2))) If (isNearRight) Then points.Add(New Point(rectRight, rectBottom)) points.Add(New Point(rectLeft, rectBottom)) points.Add(New Point(rectLeft, rectTop)) points.Add(New Point(rectRight, rectTop)) Else points.Add(New Point(rectLeft, rectTop)) points.Add(New Point(rectRight, rectTop)) points.Add(New Point(rectRight, rectBottom)) points.Add(New Point(rectLeft, rectBottom)) End If points.Add(New Point(nearEdge, -nearEdge * Math.Tan(angle - angleOffset2))) End If '矢印のポイント points.Add(New Point(Math.Cos(angle - angleOffset2) * subHypotenuse, -Math.Sin(angle - angleOffset2) * subHypotenuse)) points.Add(New Point(Math.Cos(angle - angleOffset1) * hypotenuse, -Math.Sin(angle - angleOffset1) * hypotenuse)) Return points End Function
PointCollection GetPointsForArrowCallout(double centerX, double centerY, double rectWidth, double rectHeight) { var points = new PointCollection(); double rectLeft = centerX - rectWidth / 2; double rectRight = centerX + rectWidth / 2; double rectTop = centerY - rectHeight / 2; double rectBottom = centerY + rectHeight / 2; double angle = Math.Atan2(-centerY, centerX); double angleOffset1 = 0.4; double angleOffset2 = 0.04; double arrowHeight = 0.4 * rectHeight; double hypotenuse = arrowHeight / Math.Cos(angleOffset1); double subHypotenuse = arrowHeight / Math.Cos(angleOffset2); bool isNearBottom = Math.Abs(rectTop) > Math.Abs(rectBottom); double nearHorizontalEdge = isNearBottom ? rectBottom : rectTop; bool isNearRight = Math.Abs(rectLeft) > Math.Abs(rectRight); double nearVerticalEdge = isNearRight ? rectRight : rectLeft; bool isHorizontalCrossed = Math.Abs(nearHorizontalEdge) > Math.Abs(nearVerticalEdge); double nearEdge = isHorizontalCrossed ? nearHorizontalEdge : nearVerticalEdge; int factor = nearEdge > 0 ? -1 : 1; double crossedPointOffsetToCenter = isHorizontalCrossed ? rectHeight / (2 * Math.Tan(angle)) * factor : rectWidth * Math.Tan(angle) * factor / 2; // 矢印のポイント points.Add(new Point(0, 0)); points.Add(new Point(Math.Cos(angle + angleOffset1) * hypotenuse, -Math.Sin(angle + angleOffset1) * hypotenuse)); points.Add(new Point(Math.Cos(angle + angleOffset2) * subHypotenuse, -Math.Sin(angle + angleOffset2) * subHypotenuse)); // 四角形のポイント if (isHorizontalCrossed) { points.Add(new Point(-nearEdge / Math.Tan(angle + angleOffset2), nearEdge)); if (isNearBottom) { points.Add(new Point(rectLeft, rectBottom)); points.Add(new Point(rectLeft, rectTop)); points.Add(new Point(rectRight, rectTop)); points.Add(new Point(rectRight, rectBottom)); } else { points.Add(new Point(rectRight, rectTop)); points.Add(new Point(rectRight, rectBottom)); points.Add(new Point(rectLeft, rectBottom)); points.Add(new Point(rectLeft, rectTop)); } points.Add(new Point(-nearEdge / Math.Tan(angle - angleOffset2), nearEdge)); } else { points.Add(new Point(nearEdge, -nearEdge * Math.Tan(angle + angleOffset2))); if (isNearRight) { points.Add(new Point(rectRight, rectBottom)); points.Add(new Point(rectLeft, rectBottom)); points.Add(new Point(rectLeft, rectTop)); points.Add(new Point(rectRight, rectTop)); } else { points.Add(new Point(rectLeft, rectTop)); points.Add(new Point(rectRight, rectTop)); points.Add(new Point(rectRight, rectBottom)); points.Add(new Point(rectLeft, rectBottom)); } points.Add(new Point(nearEdge, -nearEdge * Math.Tan(angle - angleOffset2))); } // 矢印のポイント points.Add(new Point(Math.Cos(angle - angleOffset2) * subHypotenuse, -Math.Sin(angle - angleOffset2) * subHypotenuse)); points.Add(new Point(Math.Cos(angle - angleOffset1) * hypotenuse, -Math.Sin(angle - angleOffset1) * hypotenuse)); return points; }
チャートの注釈をレンダリングするには、次の手順に従います。
<Chart:C1FlexChart.Layers> <Annotation:AnnotationLayer> <Annotation:AnnotationLayer.Annotations> <Annotation:Polygon x:Name="arrowCallout" Content="最低" SeriesIndex="0" PointIndex="1" Attachment="DataIndex"> <Annotation:Polygon.Style> <Chart:ChartStyle Fill="#C800FF00" Stroke="Green"/> </Annotation:Polygon.Style> </Annotation:Polygon> <Annotation:Polygon x:Name="lineCallout" Content="最高" SeriesIndex="0" PointIndex="4" Attachment="DataIndex"> <Annotation:Polygon.Style> <Chart:ChartStyle Fill="#C8FF0000" Stroke="Red" /> </Annotation:Polygon.Style> </Annotation:Polygon> </Annotation:AnnotationLayer.Annotations> </Annotation:AnnotationLayer> </Chart:C1FlexChart.Layers>
Private Sub flexChart_Rendered(sender As Object, e As RenderEventArgs) If (_engine Is Nothing) Then _engine = e.Engine SetUpAttotations() End If End Sub
private void flexChart_Rendered(object sender, C1.Xaml.Chart.RenderEventArgs e) { if (_engine == null) { _engine = e.Engine; SetUpAnnotations(); } }