9

you'll all have to excuse my ignorance as I have only recently started working with C#. I just have a question about the windows chart control, as I'm encountering a rather dumb problem.

I have a program which has a few reports that include nice looking windows Charts to represent some data. However, I have been saving these charts to files as well for various uses, by just using something like this:

chart2.SaveImage(savefilename, ChartImageFormat.Png);

My first problem lies in the fact that I am not sure how to save this as a higher resolution without first increasing the size of the chart control before saving. It would be nice to have an image of reasonable quality.

The second problem is when I do increase the size of the chart control, the available operations only seem to be able to increase the size of the actual chart, not the labels or the text. This wouldn't be a problem if I could change all of these manually, which is what I have done for a bar chart, but there is one line I can't figure out how to make thicker: the label lines on the pie chart. I have drawn an arrow to it in the following image:

http://www.bolinger.ca/chart.png

So when the chart is increased to a reasonable resolution this line is nearly invisible due to not increasing to an appropriate relative size. I feel like there should be a way to change it, but can't figure out what it would be.

Again, excuse my ignorance. If either one of these two problems could be solved then I could rest easy knowing that these pie charts look decent. Thanks!

cadrell0
  • 17,109
  • 5
  • 51
  • 69
user1260221
  • 91
  • 1
  • 1
  • 2

4 Answers4

11

Create/Duplicate a hidden (Visible = false) chart object on the form. You can even set its Top and Left properties to be off of the form. Set this control to a very high Width and Height (i.e., 2100 x 1500)... Populate and format it to your specifications. Be sure to increase the font sizes, etc. Then call SaveImage() or DrawToBitmap() from the hidden chart...

When you save this file, it will essentially be high enough resolution for most word processing, desktop pubs, printing, etc. For example, 2100 x 1500 @ 300 dpi = 7" x 5" for printing...

In your application, you can also scale it down or print it: scaling down "adds" resolution, so the image becomes sharper. Scaling up makes an image blurry or fuzzy.

I have had to rely on this technique, as it is the most consistent way to get high-res charts from the .Net chart control for printing or saving... It is a classic cheat, but it works :)

For example:

private void cmdHidden_Click(object sender, EventArgs e) {
    System.Windows.Forms.DataVisualization.Charting.Title chtTitle =
        new System.Windows.Forms.DataVisualization.Charting.Title();
    System.Drawing.Font chtFont = new System.Drawing.Font("Arial", 42);
    string[] seriesArray = { "A", "B", "C" };
    int[] pointsArray = { 1, 7, 4 };

    chart1.Visible = false;
    chart1.Width = 2100;
    chart1.Height = 1500;
    chart1.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Bright;

    chtTitle.Font = chtFont;
    chtTitle.Text = "Demographics Comparison";
    chart1.Titles.Add(chtTitle);

    chart1.Series.Clear();

    // populate chart    
    for (int i = 0; i < seriesArray.Length; i++) {
        Series series = chart1.Series.Add(seriesArray[i]);
        series.Label = seriesArray[i].ToString();
        series.Font = new System.Drawing.Font("Arial", 24);
        series.ShadowOffset = 5;
        series.Points.Add(pointsArray[i]);
    }

    // save from the chart object itself
    chart1.SaveImage(@"C:\Temp\HiddenChart.png", ChartImageFormat.Png);

    // save to a bitmap
    Bitmap bmp = new Bitmap(2100, 1500);
    chart1.DrawToBitmap(bmp, new Rectangle(0, 0, 2100, 1500));
    bmp.Save(@"C:\Temp\HiddenChart2.png");
}
Joel
  • 111
  • 1
  • 3
  • You can clone a chart by serialization as mentioned here: https://stackoverflow.com/questions/17873783/how-can-i-clone-microsoft-chart-control – flodis May 05 '20 at 14:51
3

Here's a class I made to make a larger graph, save it, then restore the graph. Works well for my my purposes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Chart;
using System.Drawing.Imaging;
using System.Windows.Forms.DataVisualization.Charting;
using System.Windows.Forms;

namespace Simple_Grapher
{
    class saveQualityChartImage
    {
        Chart theChart;
        System.Drawing.Font oldFont1 = new System.Drawing.Font("Trebuchet MS", 35F, System.Drawing.FontStyle.Bold);
        System.Drawing.Font oldFont2 = new System.Drawing.Font("Trebuchet MS", 15F, System.Drawing.FontStyle.Bold);
        System.Drawing.Font oldFont3 = new System.Drawing.Font("Trebuchet MS", 35F, System.Drawing.FontStyle.Bold);
        System.Drawing.Font oldLegendFont = new System.Drawing.Font("Trebuchet MS", 35F, System.Drawing.FontStyle.Bold);

        int oldLineWidth1;
        int oldLineWidth2;
        int oldLineWidth3;
        int oldLineWidth4;

        int oldWidth;
        int oldHeight;
        public saveQualityChartImage(Chart inputChart)
        {
            if (!(inputChart.Series.Count > 0))
            {
                return;
            }
            theChart = inputChart;
            if (inputChart.Titles.Count > 0)
            {
                oldFont1 = inputChart.Titles[0].Font;
            }
            oldFont2 = inputChart.ChartAreas[0].AxisX.LabelStyle.Font;
            oldFont3 = inputChart.ChartAreas[0].AxisX.TitleFont;
            if (theChart.Legends.Count > 0)
            {
                oldLegendFont = theChart.Legends["Legend"].Font;
            }
            oldLineWidth1 = theChart.ChartAreas[0].AxisX.LineWidth;
            oldLineWidth2 = theChart.ChartAreas[0].AxisX.MajorTickMark.LineWidth;
            oldLineWidth3 = theChart.Series[0].BorderWidth;
            oldLineWidth4 = theChart.ChartAreas[0].AxisY.MajorGrid.LineWidth;
            oldWidth = theChart.Width;
            oldHeight = theChart.Height;

            saveimage();
        }

        public void saveimage()
        {
            theChart.Visible = false;
            System.Drawing.Font chtFont = new System.Drawing.Font("Trebuchet MS", 35F, System.Drawing.FontStyle.Bold);
            System.Drawing.Font smallFont = new System.Drawing.Font("Trebuchet MS", 15F, System.Drawing.FontStyle.Bold);
            if (theChart.Titles.Count > 0)
            {
                theChart.Titles[0].Font = chtFont;
            }

            theChart.ChartAreas[0].AxisX.TitleFont = chtFont;
            theChart.ChartAreas[0].AxisX.LineWidth = 3;
            theChart.ChartAreas[0].AxisX.MajorGrid.LineWidth = 3;
            theChart.ChartAreas[0].AxisX.LabelStyle.Font = smallFont;
            theChart.ChartAreas[0].AxisX.MajorTickMark.LineWidth = 3;

            theChart.ChartAreas[0].AxisY.TitleFont = chtFont;
            theChart.ChartAreas[0].AxisY.LineWidth = 3;
            theChart.ChartAreas[0].AxisY.MajorGrid.LineWidth = 3;
            theChart.ChartAreas[0].AxisY.LabelStyle.Font = smallFont;
            theChart.ChartAreas[0].AxisY.MajorTickMark.LineWidth = 3;
            if (theChart.Legends.Count > 0)
            {
                theChart.Legends["Legend"].Font = smallFont;
            }


            foreach (Series series in theChart.Series)
            {
                series.BorderWidth = 3;

            }

            theChart.Width = 1800;
            theChart.Height = 1200;

            SaveFileDialog save = new SaveFileDialog();
            save.DefaultExt = ".png";
            if (save.ShowDialog() == DialogResult.OK)
            {
                theChart.SaveImage(save.FileName, ChartImageFormat.Png);
            }
            resetOldValues();

        }

        private void resetOldValues()
        {
            if (theChart.Titles.Count > 0)
            {
                theChart.Titles[0].Font = oldFont1;
            }

            theChart.ChartAreas[0].AxisX.TitleFont = oldFont3;
            theChart.ChartAreas[0].AxisX.LineWidth = oldLineWidth1;
            theChart.ChartAreas[0].AxisX.MajorGrid.LineWidth = oldLineWidth4;
            theChart.ChartAreas[0].AxisX.LabelStyle.Font = oldFont2;
            theChart.ChartAreas[0].AxisX.MajorTickMark.LineWidth = oldLineWidth2;

            theChart.ChartAreas[0].AxisY.TitleFont = oldFont3;
            theChart.ChartAreas[0].AxisY.LineWidth = oldLineWidth1;
            theChart.ChartAreas[0].AxisY.MajorGrid.LineWidth = oldLineWidth4;
            theChart.ChartAreas[0].AxisY.LabelStyle.Font = oldFont2;
            theChart.ChartAreas[0].AxisY.MajorTickMark.LineWidth = oldLineWidth2;
            if (theChart.Legends.Count > 0)
            {
                theChart.Legends["Legend"].Font = oldLegendFont;
            }



            foreach (Series series in theChart.Series)
            {
                series.BorderWidth = oldLineWidth3;

            }

            theChart.Width = oldWidth;
            theChart.Height = oldHeight;
            theChart.Visible = true;   
        }  
    }
}
Mark
  • 1,374
  • 11
  • 12
1

Try setting chart2.RenderTransform = new ScaleTransform(10,10) and save it. This should also make your lines bigger.

Ray
  • 2,974
  • 20
  • 26
0

I'm going to add this as a class to save a chart as an image, because most of the examples I have found on here were using System.Windows.Forms. This uses System.Web.UI.DataVisualization.Charting. There are some slight differences between the two that throw errors if you are trying to adapt the examples on this page to a web server. Also, a lot of the other SO questions/examples were not using an IEnumerable data source.

The data source here is just a plain DataTable returned as AsEnumerable() (e.g. return dt.AsEnumerable();).

Titles and Legends were added to the saved image, as well.

This is generating two images from one data source. A pressure and temperature chart. The pressure chart below is actual output from this code.

  public static void ChartToImage(Certification.Chart chartData, string hashId, string orderId, string chartNo, string multiChart = "0")
    {
        string filepath = System.Web.Hosting.HostingEnvironment.MapPath("~/docs/");
        string fileNamePress = filepath + chartData.HashId + "p.png";
        string fileNameTemp = filepath + chartData.HashId + "t.png";

        System.Web.UI.DataVisualization.Charting.Chart chart1 = new System.Web.UI.DataVisualization.Charting.Chart();

        System.Web.UI.DataVisualization.Charting.Title cht1Title = new
        System.Web.UI.DataVisualization.Charting.Title();
        System.Drawing.Font cht1Font = new System.Drawing.Font("Arial", 18);


        System.Web.UI.DataVisualization.Charting.Legend cht1Legend = new
        System.Web.UI.DataVisualization.Charting.Legend();
        System.Drawing.Font cht1LegendFont = new System.Drawing.Font("Arial", 18);

        System.Collections.IEnumerable enumerableTable = ChartDAL.ChartIEList(hashId);

        System.DateTime x = new System.DateTime(2008, 11, 21);

        var chartArea1 = new System.Web.UI.DataVisualization.Charting.ChartArea();
        chart1.ChartAreas.Add(chartArea1);
        chart1.DataSource = enumerableTable;
        
        chart1.Series.Add("Series1");
        chart1.Series["Series1"].ChartType = SeriesChartType.Spline;
        chart1.Series["Series1"].XValueType = ChartValueType.Time;
        chart1.Series["Series1"].Points.AddXY(x.ToUniversalTime(), "tstamp");
        chart1.Series["Series1"].XValueMember = "tstamp";
        chart1.Series["Series1"].YValueMembers = "pressure";
        chart1.Series["Series1"].IsValueShownAsLabel = false;
        chart1.Series["Series1"].Name = "PSI"; // Normally PSI. In the future, pull pressure testing unit from enumerableTable;


        cht1Title.Font = cht1Font;
        cht1Title.Text = "Pressure Chart"; // In future, pull job number/chart number from database;
        chart1.Titles.Add(cht1Title);

        cht1Legend.Font = cht1Font;            
        chart1.Legends.Add(cht1Legend); // chart1.Series["Series1"].Name;

        chart1.Width = 900;
        chart1.Height = 400;
        chart1.DataBind();
        
        chart1.SaveImage(fileNamePress, ChartImageFormat.Png);

        System.Web.UI.DataVisualization.Charting.Chart chart2 = new System.Web.UI.DataVisualization.Charting.Chart();

        System.Web.UI.DataVisualization.Charting.Title cht2Title = new
        System.Web.UI.DataVisualization.Charting.Title();
        System.Drawing.Font cht2Font = new System.Drawing.Font("Arial", 18);

        System.Web.UI.DataVisualization.Charting.Legend cht2Legend = new
        System.Web.UI.DataVisualization.Charting.Legend();
        System.Drawing.Font cht2LegendFont = new System.Drawing.Font("Arial", 18);

        var chartArea2 = new System.Web.UI.DataVisualization.Charting.ChartArea();
        chart2.ChartAreas.Add(chartArea2);
        chart2.DataSource = enumerableTable;

        chart2.Series.Add("Series1");
        chart2.Series["Series1"].ChartType = SeriesChartType.Spline;
        chart2.Series["Series1"].XValueType = ChartValueType.Time;
        chart2.Series["Series1"].Points.AddXY(x.ToUniversalTime(), "tstamp");
        chart2.Series["Series1"].XValueMember = "tstamp";
        chart2.Series["Series1"].YValueMembers = "temperature";
        chart2.Series["Series1"].IsValueShownAsLabel = false;
        chart2.Series["Series1"].Name = "Celsius"; // Normally Celsius. In the future, pull temperature testing unit from enumerableTable;

        cht2Title.Font = cht2Font;
        cht2Title.Text = "Temperature Chart"; // In future, pull job number/chart number from database;
        chart2.Titles.Add(cht2Title);

        cht2Legend.Font = cht2Font;            
        chart2.Legends.Add(cht2Legend); // chart2.Series["Series1"].Name;

        chart2.Width = 900;
        chart2.Height = 400;
        chart2.DataBind();

        chart1.SaveImage(fileNamePress, ChartImageFormat.Png);
        chart2.SaveImage(fileNameTemp, ChartImageFormat.Png);


    }   

enter image description here

user1544428
  • 110
  • 1
  • 8