简体   繁体   中英

C# WinForms application: dual-color column in stacked chart

I am an hobbyist .NET developer. I am developing a Windows Forms app and this picture shows want I want to use a chart control for: 图表

Basically, I want to change the upper part of every column which is taller than a certain threshold (400 in the example above). I have searched around a bit, but no joy yet. Any ideas?

EDIT: I have a graceful workaround, but it's still frustrating not to have something available in the control itself. I have created two series, red and blue. When I add a point I check if the height is bigger than the threshold. If yes, I add the point to the red series, and I also add a point of threshold height to the blue series. If not, I add it to the blue series as is. The code can explain better than me:

    private void AddPointToChart(Chart chart, int x, int y)
    {
        if (this.threshold < y)
        {
            chart.Series[1].Points.AddXY(x, y);
            y = this.threshold;
        }

        chart.Series[0].Points.AddXY(x, y);
    }

As per the original question, StackedColumn is the way to go!

And for your edit, on option (not an easy one) is to override virtual methods of Chart to add your own behaviour. I came up with an example after some experimentation:

public class ThresholdColumnChart : Chart
{
    private double _threshold = 50d;

    public double Threshold
    {
        get { return _threshold; }
        set { _threshold = value; Invalidate(); }
    }

    public ThresholdColumnChart() : base() { }

    protected override void OnCustomize()
    {
        base.OnCustomize();

        if (Series.Count != 1)
            return;

        Series.Add(new Series());

        foreach (var dataPoint in Series[0].Points)
        {
            var newDataPoint = new DataPoint();

            newDataPoint.XValue = dataPoint.XValue;
            newDataPoint.YValues[0] = (dataPoint.YValues[0] > _threshold ? 
                 dataPoint.YValues[0] - _threshold : 0);

            Series[1].Points.Add(newDataPoint);

            if (dataPoint.YValues[0] > _threshold)
                dataPoint.YValues[0] = _threshold;
        }

        Series[0].ChartType = SeriesChartType.StackedColumn;
        Series[1].ChartType = SeriesChartType.StackedColumn;
        Series[1].Color = Color.Red;
    }

    protected override void OnPostPaint(ChartPaintEventArgs e)
    {
        base.OnPostPaint(e);

        if (!(e.ChartElement is ThresholdBarChart))
            return;

        if (Series.Count != 2)
            return;

        for (int i = 0; i < Series[0].Points.Count; i++)
            Series[0].Points[i].YValues[0] += Series[1].Points[i].YValues[0];

        Series.Remove(Series[1]);
    }
}

In short the OnCustomize(...) method is called just before the chart is drawn (before OnPrePaint(...) ) at which point it creates a second series to be used for drawing. The new series is constructed with the threshold in mind, and the first series is reduced not to exceed the threshold just like the example in your edit.

After the chart is done painting OnPostPaint(...) will be called and the first series will be restored to it's original values, and the "extra" series is removed.

Both OnPrePaint(...) and OnPostPaint are actually called multiple times when a chart is drawn, hence why I had to put if (!(e.ChartElement is ThresholdBarChart)) return; in; to make sure we only remove the series once the chart has finnished painting.

After adding the code and compiling once you will get a new usercontrol added to the design toolbox called "ThresholdColumnChart". You can set the threshold from the Property "Threshold" (also in design). I wouldnt trust this code with my life but it should serve as a starting point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM