[]
        
(Showing Draft Content)

WinForms Floating Bar Chart

Floating bar charts are charts with a single or multiple bars floating between a minimum and maximum value instead of being connected to the axis. It displays information as a range of data by plotting two Y-values(low and high) per data point. Floating bars can be useful to show highs and lows in a data set, such as daily high and low temperatures, stock prices, blood pressure readings, etc.


WinForms Floating Bar Chart


In FlexChart, WinForms floating bar chart can be implemented using the Series class. To begin with, create a new Series object and specify its properties. Then, use the SymbolRendering event provided by the Series class to plot the data points on the chart.


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

Note that the above sample code uses a custom method named GetTemperatureData to supply data to the chart. You can set up the data source as per your requirements.


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