I have a C# program that is a console application, but makes use of a Chart object from System.Windows.Forms.DataVisualization.Charting
, without attaching the Chart
to a Form
.
Basically I need to generate a doughnut graph on the Chart
control, print some text inside the centre of the doughnut, then save the graph plus the text into a file on disk.
If I create a TextAnnotation
in the PrePaint
event, then save the chart to disk with DrawToBitmap
, the overlaid text is not shown in the file on disk, even if I use various combinations of Chart.Flush
and Chart.Update
, etc. From what I can tell, the event is being fired and the TextAnnotation
code is being run.
On the other hand, if I don't use events at all, instead get a Graphics
object from Chart.CreateGraphics
, then Graphics.DrawString
to print the text, then Graphics.Flush
and Chart.DrawToBitmap
, the text is still not displayed.
I am guessing that this is because Chart.DrawToBitmap
doesn't know about the extra stuff drawn with Chart.CreateGraphics
, even though I assumed Graphics.Flush
would take care of that.
What would be the best way to save the graph and the text?
EDIT: Added code as requested:
static void Main(string[] args)
{
String[] labels = { "Green", "Red", "Yellow", "Dark Red", "Blue" };
Color[] colours = { Color.Green, Color.Red, Color.Yellow, Color.DarkRed, Color.Blue };
Random rng = new Random();
Chart chart = new Chart();
chart.Size = new Size(320, 320);
ChartArea area = new ChartArea();
Series series = new Series();
chart.ChartAreas.Add(area);
series.ChartArea = area.Name;
series.ChartType = SeriesChartType.Doughnut;
series.IsValueShownAsLabel = true;
int total = 0;
for (int i = 0; i != labels.Length; i++)
{
int value = rng.Next(0, 50);
DataPoint p = new DataPoint(0, value);
total += value;
p.Color = colours[i];
p.Label = String.Empty;
p.Font = new Font("Microsoft Sans Serif", 10, FontStyle.Bold);
series.Points.Add(p);
}
series.Tag = total;
chart.Series.Add(series);
chart.Refresh();
using (Graphics g = chart.CreateGraphics())
{
String str = series.Tag.ToString();
Font font = new Font("Microsoft Sans Serif", 32, FontStyle.Bold);
SizeF strSize = g.MeasureString(str, font);
int strX = 100; int strY = 100;
g.DrawString(str, font, new SolidBrush(Color.Black), strX, strY);
g.DrawRectangle(new Pen(Color.Black), new Rectangle(strX, strY, (int)strSize.Width, (int)strSize.Height));
g.Flush();
}
String chartFilename = "chart.bmp";
String chartPath = Path.Combine("C:\\Temp", chartFilename);
using (Bitmap bmp = new Bitmap(chart.Width, chart.Height))
{
chart.DrawToBitmap(bmp, chart.Bounds);
bmp.Save(chartPath);
}
System.Diagnostics.Process.Start("mspaint.exe", chartPath);
}
I suspect the chart is drawing over the top of your label. When you call DrawToBitmap, the chart is only going to consider visuals it knows about and not elements drawn over the top afterwards.
You need to reverse drawing order. i.e.
Code:
using (Bitmap bmp = new Bitmap(chart.Width, chart.Height))
{
chart.DrawToBitmap(bmp, chart.Bounds); // draw chart into bitmap first!
using (Graphics g = Graphics.FromImage(bmp)) // <--- new
{
// now draw label
String str = series.Tag.ToString();
Font font = new Font("Microsoft Sans Serif", 32, FontStyle.Bold);
SizeF strSize = g.MeasureString(str, font);
int strX = 100; int strY = 100;
g.DrawString(str, font, new SolidBrush(Color.Black), strX, strY);
g.DrawRectangle(new Pen(Color.Black), new Rectangle(strX, strY, (int)strSize.Width, (int)strSize.Height));
g.Flush();
}
bmp.Save(chartPath);
}
EDIT: Be sure to follow Jimi's suggestion below in the comments to:
"reference
System.Drawing.Imaging
, to havePixelFormat
andImageFormat
available. At this moment, the image is saved as with a .bmp extension, while it's a .png file (the default)"