FlexChart for WinForms
浮動棒チャート
WinForms のチャートタイプ > 特殊チャート > 浮動棒チャート

浮動棒グラフは、1 つまたは複数の棒が、軸に接しないで、最小値と最大値の間に浮いているように表示されるチャートです。情報は一定の範囲のデータとして表示され、データポイントごとに 2 つの Y 値(低値と高値)がプロットされます。浮動棒グラフは、1 日の最高/最低気温、株価、血圧値など、最高値と最低値から成るデータセットを表示する場合に便利です。

WinForms 浮動棒グラフ

FlexChart では、Series クラスを使用して WinForms 浮動棒グラフを実装できます。初めに、新しい Series オブジェクトを作成し、そのプロパティを指定します。次に、Series クラスから提供される SymbolRendering イベントを使用して、グラフにデータポイントをプロットします。

private void Form1_Load(object sender, EventArgs e)
{
    string[] cities = { "Chicago", "New York" };
    List<CityDataItem> data = GetTemperatureData(cities, true, 7, true);
    this.flexChart1.AxisY.Min = data.Select(x => x.Data.Min(y => y.LowTemp)).Min();
    this.flexChart1.AxisY.Max = data.Select(x => x.Data.Max(y => y.HighTemp)).Max();
    foreach (var dataItem in data)
    {
        Series series = new Series()
        {
            Binding = "HighTemp",
            BindingX = "Date",
            Name = dataItem.Name,
            DataSource = dataItem.Data,
        };
        series.SymbolRendering += Series_SymbolRendering;
        this.flexChart1.Series.Add(series);
    }

    this.flexChart1.DataLabel.Content = "{seriesName}";
    this.flexChart1.DataLabel.Position = LabelPosition.Bottom;
    this.flexChart1.LabelRendering += FlexChart1_LabelRendering;

    this.flexChart1.Header.Content = "Weather Report : Monthly Temperatures";
    this.flexChart1.AxisY.Format = "0 °F";
    this.flexChart1.Options.ClusterSize = new ElementSize { SizeType = ElementSizeType.Percentage, Value = columnWidthPercentage * 100 };
    this.flexChart1.DataLabel.Overlapping = LabelOverlapping.Show;
}
private void FlexChart1_LabelRendering(object sender, RenderDataLabelEventArgs e)
{
    var temp = (Temperature)e.Item;
    e.Text = string.Format("{0:0}:{1:0}", temp.LowTemp, temp.HighTemp);
}

private void Series_SymbolRendering(object sender, RenderSymbolEventArgs e)
{
    e.Cancel = true;
    Temperature temperature = (Temperature)e.Item;
    var width = this.flexChart1.PlotRect.Width / ((List<Temperature>)this.flexChart1.Series[0].DataSource).Count * columnWidthPercentage / this.flexChart1.Series.Count;
    var bottom = 0d;
    bottom = this.flexChart1.AxisY.Convert(temperature.LowTemp);
    e.Engine.DrawRect(e.Point.X - width / 2 - 2, e.Point.Y, width - 4, bottom - e.Point.Y);
}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim cities As String() = {"Chicago", "New York"}
    Dim data As List(Of CityDataItem) = GetTemperatureData(cities, True, 7, True)
    'AddressOf Me.flexChart1.AxisY.Min = data.[Select](Function(x) x.Data.Min(Function(y) y.LowTemp)).Min()
    'AddressOf Me.flexChart1.AxisY.Max = data.[Select](Function(x) x.Data.Max(Function(y) y.HighTemp)).Max()
    For Each dataItem As CityDataItem In data
        Dim series As New Series() With {
              .Binding = "HighTemp",
              .BindingX = "Date",
              .Name = dataItem.Name,
              .DataSource = dataItem.Data
        }
        AddHandler series.SymbolRendering, AddressOf Series_SymbolRendering
        Me.flexChart1.Series.Add(series)
    Next

    Me.flexChart1.DataLabel.Content = "{seriesName}"
    Me.flexChart1.DataLabel.Position = LabelPosition.Bottom
    AddHandler Me.flexChart1.LabelRendering, AddressOf FlexChart1_LabelRendering

    Me.flexChart1.Header.Content = "Weather Report : Monthly Temperatures"
    Me.flexChart1.AxisY.Format = "0 °F"
    Me.flexChart1.Options.ClusterSize = New ElementSize() With {
          .SizeType = ElementSizeType.Percentage,
          .Value = columnWidthPercentage * 100
    }
    Me.flexChart1.DataLabel.Overlapping = LabelOverlapping.Show
End Sub
Private Sub FlexChart1_LabelRendering(sender As Object, e As RenderDataLabelEventArgs)
    Dim temp As Temperature = DirectCast(e.Item, Temperature)
    e.Text = String.Format("{0:0}:{1:0}", temp.LowTemp, temp.HighTemp)
End Sub

Private Sub Series_SymbolRendering(sender As Object, e As RenderSymbolEventArgs)
    e.Cancel = True
    Dim temperature As Temperature = DirectCast(e.Item, Temperature)
    Dim width As Single = Me.flexChart1.PlotRect.Width / DirectCast(Me.flexChart1.Series(0).DataSource, List(Of Temperature)).Count * columnWidthPercentage / Me.flexChart1.Series.Count
    Dim bottom As Double = 0.0
    bottom = Me.flexChart1.AxisY.Convert(temperature.LowTemp)
    e.Engine.DrawRect(e.Point.X - width / 2 - 2, e.Point.Y, width - 4, bottom - e.Point.Y)
End Sub

上記のサンプルコードは、GetTemperatureData という名前のカスタムメソッドを使用してチャートにデータを提供しています。要件に基づいてデータソースを設定できます。

        
private Random rnd = new Random();
public List<CityDataItem> GetTemperatureData(string[] cities, bool monthly = false, int count = 30, bool isFahrenheit = false)
{
    var data = new List<CityDataItem>();
    var startDate = new DateTime(2017, 1, 1);
    foreach (string city in cities)
    {
        var dataItem = new CityDataItem() { Name = city };
        for (int i = 0; i < count; i++)
        {
            var temp = new Temperature();
            DateTime date;
            if (monthly)
                date = startDate.AddMonths(i);
            else
                date = startDate.AddDays(i);
            temp.Date = date;
            if (date.Month <= 8)
                temp.HighTemp = rnd.Next(3 * date.Month, 8 * date.Month);
            else
                temp.HighTemp = rnd.Next((13 - date.Month - 2) * date.Month, (13 - date.Month) * date.Month);
            temp.LowTemp = temp.HighTemp - rnd.Next(6, 8);
            temp.Precipitation = (date.Month < 4 || date.Month > 8) ? rnd.Next(100, 150) : rnd.Next(150, 200);
            if (isFahrenheit) temp.HighTemp = temp.HighTemp * 1.8 + 32;
            dataItem.Data.Add(temp);
        }
        data.Add(dataItem);
    }
    return data;
}
Private rnd As New Random()
Public Function GetTemperatureData(cities As String(), Optional monthly As Boolean = False, Optional count As Integer = 30, Optional isFahrenheit As Boolean = False) As List(Of CityDataItem)
    Dim data As List(Of CityDataItem) = New List(Of CityDataItem)()
    Dim startDate As DateTime = New DateTime(2017, 1, 1)
    For Each city As String In cities
        Dim dataItem As CityDataItem = New CityDataItem() With {
              .Name = city
        }
        For i As Integer = 0 To count - 1
            Dim temp As Temperature = New Temperature()
            Dim [date] As DateTime
            If monthly Then
                [date] = startDate.AddMonths(i)
            Else
                [date] = startDate.AddDays(i)
            End If
            temp.[Date] = [date]
            If [date].Month <= 8 Then
                temp.HighTemp = rnd.[Next](3 * [date].Month, 8 * [date].Month)
            Else
                temp.HighTemp = rnd.[Next]((13 - [date].Month - 2) * [date].Month, (13 - [date].Month) * [date].Month)
            End If
            temp.LowTemp = temp.HighTemp - rnd.[Next](6, 8)
            temp.Precipitation = If(([date].Month < 4 OrElse [date].Month > 8), rnd.[Next](100, 150), rnd.[Next](150, 200))
            If isFahrenheit Then
                temp.HighTemp = temp.HighTemp * 1.8 + 32
            End If
            dataItem.Data.Add(temp)
        Next
        data.Add(dataItem)
    Next
    Return data
End Function