FlexChart for WPF
吹き出しの作成
FlexChart > FlexChart の操作 > FlexChart の要素 > 注釈 > 吹き出しの作成

チャート内で吹き出しを使用して、データ系列や個別のデータポイントの詳細を読みやすい形式で表示できます。データポイントに接続された吹き出しは、チャート領域内の見た目の乱雑さを最小限に抑え、チャートデータを見やすく、かつわかりやすくします。FlexChart では、Polygon 型の注釈をカスタマイズして、直線または矢印の接続線が付いた吹き出しをチャートに作成できます。

この例では、クイックスタートで作成したサンプルを使用して、矢印付き吹き出しと接続線付きの多角形注釈を作成します。それには、Points プロパティと ContentCenter プロパティを使用して、それぞれ多角形の頂点の座標と注釈のコンテンツの中心を定義します。

それぞれのデータポイントに接続される吹き出しを作成するには、次の手順に従います。

次の図に、矢印と接続線でデータポイントに接続される多角形注釈を示します。

Annotation connector

手順 1:接続線付きの注釈の作成

直線付きの吹き出しを作成するには、次のコードを使用します。

        ...
        ' 多角形の線吹き出しの注釈を作成する
        Dim lineCallout = New C1.WPF.Chart.Annotation.Polygon() With {
     .Content = "最高",
     .Style = New ChartStyle() With {
         .Fill = New SolidColorBrush(Colors.Red) With {
             .Opacity = 200.0 / 255
        },
         .Stroke = New SolidColorBrush(Colors.Red)
    },
     .Attachment = AnnotationAttachment.DataIndex,
     .SeriesIndex = 0,
     .PointIndex = 1,
     .ContentCenter = New Point(25, -40),
     .Points = New PointCollection(New Point() {New Point(0, 0), 
     New Point(25, -25), New Point(50, -25), New Point(50, -50), 
     New Point(25, -75), New Point(0, -50),
        New Point(0, -25), New Point(25, -25), New Point(0, 0)})
}
        ...
    ...
    // 多角形の線吹き出しの注釈を作成する
    var lineCallout = new C1.WPF.Chart.Annotation.Polygon()
    {
        Content = "最高",
        //多角形の線吹き出しの注釈のスタイル設定
        Style = new ChartStyle()
        {
            Fill = new SolidColorBrush(Colors.Red) { Opacity = 200.0 / 255 },
            Stroke = new SolidColorBrush(Colors.Red),
        },
        Attachment = AnnotationAttachment.DataIndex,
        SeriesIndex = 0,
        PointIndex = 1,
        ContentCenter = new Point(25, -40),
    Points = new PointCollection(new Point[] { new Point(0, 0), new Point(25, -25),
new Point(50, -25), new Point(50, -50), new Point(25, -75),
new Point(0, -50), new Point(0, -25), new Point(25, -25), new Point(0, 0) })
    };
    ...

先頭に戻る

手順 2:矢印付き注釈吹き出しの作成

  1. 矢印付きの吹き出しを作成するには、次のコードを使用します。
    Private Sub SetUpAnnotations()
        annotationLayer.Annotations.Clear()
        ' 多角形の矢印吹き出しの注釈を作成する 
        Dim contentCenter = New Point(25, -50)
        '矢印吹き出しの注釈のスタイル設定
        Dim arrowCallout = New Annotation.Polygon() With {
         .Content = "最低",
         .Style = New ChartStyle() With {
             .Fill = New SolidColorBrush(Colors.Green) With {
                 .Opacity = 200.0 / 255
            },
             .Stroke = New SolidColorBrush(Colors.Green)
        },
         .Attachment = AnnotationAttachment.DataIndex,
         .SeriesIndex = 1,
         .PointIndex = 1,
         .ContentCenter = contentCenter,
         .Points = GetPointsForArrowCallout(contentCenter.X, 
         contentCenter.Y, "最低")
    }
        ...
    
    private void SetUpAnnotations()
    {
        annotationLayer.Annotations.Clear();
        // 多角形の矢印吹き出しの注釈を作成する
        var contentCenter = new Point(25, -50);
        var arrowCallout = new Annotation.Polygon()
        {
            Content = "最低",
            //矢印吹き出しの注釈のスタイル設定
            Style = new ChartStyle()
            {
             Fill = new SolidColorBrush(Colors.Green) { Opacity = 200.0 / 255 },
             Stroke = new SolidColorBrush(Colors.Green),
            },
            Attachment = AnnotationAttachment.DataIndex,
            SeriesIndex = 1,
            PointIndex = 1,
            ContentCenter = contentCenter,
            Points = GetPointsForArrowCallout(contentCenter.X, contentCenter.Y, "最低")
        };
        ...
    

  2. GetPointsForArrowCallout() メソッドを定義して、矢印付きの吹き出しのポイントを指定します。
    1. 矢印付きの吹き出し内のコンテンツ文字列のサイズを測定し、これを再利用して、矢印付きの注釈のサイズを計算して設定するには、次のコードを使用します。
      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, 
          CSng(size.Width) + 10, CSng(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);
      }
      

    2. 矢印付きの注釈のサイズとポイントを計算するには、次のようにオーバーロードメソッド GetPointsForArrowCallout() を定義します。
      Private Function GetPointsForArrowCallout(centerX As Double, 
      centerY As Double, rectWidth As Double, rectHeight As Double) 
      As PointCollection
          Dim points = 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, 
          rectBottom, rectTop)
          Dim isNearRight As Boolean = Math.Abs(rectLeft)
           > Math.Abs(rectRight)
          Dim nearVerticalEdge As Double = If(isNearRight,
           rectRight, rectLeft)
          Dim isHorizontalCrossed As Boolean = Math.Abs(nearHorizontalEdge)
           > Math.Abs(nearVerticalEdge)
          Dim nearEdge As Double = If(isHorizontalCrossed, 
          nearHorizontalEdge, nearVerticalEdge)
      
          Dim factor As Integer = If(nearEdge > 0, -1, 1)
          Dim crossedPointOffsetToCenter As Double = If(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 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;
      }
      

先頭に戻る

手順 3:チャートへの注釈のレンダリング

チャートの注釈をレンダリングするには、次の手順に従います。

  1. レンダリングエンジンのグローバルフィールドを定義します。
    Dim _engine As IRenderEngine
    
    IRenderEngine _engine;
    

  2. 次のコードを使用して、AnnotationLayer クラスのインスタンスを作成します。
    XAML
    コードのコピー
    <c1:C1FlexChart.Layers>
        <c1:AnnotationLayer x:Name="annotationLayer" />
    </c1:C1FlexChart.Layers>
    

  3. 次のコードを使用して、吹き出し注釈を annotationLayer に追加します。
        annotationLayer.Annotations.Add(arrowCallout)
        annotationLayer.Annotations.Add(lineCallout)
    End Sub
    
        annotationLayer.Annotations.Add(arrowCallout);
        annotationLayer.Annotations.Add(lineCallout);
    }
    

  4. 吹き出しをレンダリングするには、FlexChart の Rendered イベントで次のコードを使用します。
    Private Sub flexChart_Rendered(sender As Object, e As 
    C1.WPF.Chart.RenderEventArgs) Handles flexChart.Rendered
        If _engine Is Nothing Then
            _engine = e.Engine
            SetUpAnnotations()
        End If
    End Sub
    
    private void flexChart_Rendered(object sender, 
    C1.WPF.Chart.RenderEventArgs e)
    {
        if (_engine == null)
        {
            _engine = e.Engine;
            SetUpAnnotations();
        }
    }
    

先頭に戻る