Blog

Tagged by 'bar chart'

  • As promised, I have been busy creating a custom web part to display Microsoft Charts within my SharePoint reporting dashboard. Before you even think of wanting to use Microsoft Chart in a SharePoint environment, there are a few boring tasks you need to carry out beforehand:

    Step 1: Copy Charting Files To SharePoint

    1. Download Microsoft Chart here.
    2. Ensure the System.Web.DataVisualization.dll is in the GAC of your SharePoint server. If not, go to your Microsoft Chart installation directory (C:\Program Files\Microsoft Chart Controls\Assemblies). You will find all the dll’s here.
    3. Copy the charting images from the MS Chart project directory.

    Image Directory 4. On your SharePoint server, go to the 12 hive and create a new folder called “MicrosoftChartControls” in the Images directory.

    SharePoint Image Directory

    Step 2: Modify SharePoint’s Web.Config File

    1. Add “System.Web.DataVisualization” to the SafeControls section.
    <SafeControls>
        ...
        ... 
        <SafeControl Assembly="System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"  Namespace="System.Web.UI.DataVisualization.Charting" TypeName="*" Safe="True" AllowRemoteDesigner="True"/>
    </SafeControls>
    
    1. Add the “ChartImg.axd” path to httpHandlers section.
    <httpHandlers>
        ...
        ...
        <add verb="*" path="ChartImg.axd" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
    </httpHandlers>
    
    1. Add a new AppSetting after the </system.workflow.componentmodel.workflowcompiler> tag. As you can see, this AppSetting contains the URL to where our charting images are stored.
    </System.Workflow.ComponentModel.WorkflowCompiler>
    <appSettings>
        <add key="ChartImageHandler" value="storage=memory;timeout=20;URL=/_layouts/Images/MicrosoftChartControls/" />
    </appSettings>
    
    1. Lastly, ensure you add your own custom web part charting control to the SafeControls section as I have done.
    <SafeControl Assembly="MOSSCharts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d463a6b3aa294272" Namespace="MOSSCharts" TypeName="*" Safe="True" AllowRemoteDesigner="True" />
    

    If you still have problems with SharePoint failing to recognise the charting API, view this MSDN Forum posting.

    My Custom Chart Example – Bar Chart

    I decided it would be more useful to create a charting web part that would display graphical data from a database, instead of using randomised or hard coded values. My charting web part contains the following properties:

    • Chart Title
    • Database Connection String – in the real world you wouldn’t include this property. But it just makes it so much more easier to change database connection strings.
    • Series 1 Query – will contain a SQL Query based on the results you want returned.
    • Series 1 X Data – the column data you want displayed in the Bar Chart X Axis.
    • Series 1 Y Data - the column data you want displayed in the Bar Chart Y Axis.

    Chart Properties

    Based on the properties above, your chart will look something like this:

    Custom Chart

    This is how you can create the Bar Chart web part:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.DataVisualization.Charting;
    using System.Drawing;
    using System.Data;
    using System.Data.Sql;
    using Microsoft.SharePoint;
    using System.Web.UI.WebControls.WebParts;
    using System.Data.SqlClient;
    using System.ComponentModel;
    using Microsoft.SharePoint.WebPartPages;
    using System.Web.UI.WebControls;
     
    namespace MOSSCharts
    {
        public class BarChart : Microsoft.SharePoint.WebPartPages.WebPart
        {
            private string chartName;
            [WebBrowsable(true), Personalizable(true)]
            [Category("Chart Details")]
            [FriendlyName("Chart Title")]
            public string ChartName
            {
                get { return chartName; }
                set { chartName = value; }
            }
     
            private string connectionString;
            [WebBrowsable(true), Personalizable(true)]
            [Category("Database Details")]
            public string ConnectionString
            {
                get { return connectionString; }
                set { connectionString = value; }
            }
     
            private string querySeries1;
            [WebBrowsable(true), Personalizable(true)]
            [Category("Series 1 Data")]
            [FriendlyName("Series 1 Query")]
            public string QuerySeries1
            {
                get { return querySeries1; }
                set { querySeries1 = value; }
            }
     
            private string xColumnDataSeries1;
            [WebBrowsable(true), Personalizable(true)]
            [Category("Series 1 Data")]
            [FriendlyName("Series 1 X Data")]
            public string XColumnDataSeries1
            {
                get { return xColumnDataSeries1; }
                set { xColumnDataSeries1 = value; }
            }
     
            private string yColumnDataSeries1;
            [WebBrowsable(true), Personalizable(true)]
            [Category("Series 1 Data")]
            [FriendlyName("Series 1 Y Data")]
            public string YColumnDataSeries1
            {
                get { return yColumnDataSeries1; }
                set { yColumnDataSeries1 = value; }
            }
     
            protected override void CreateChildControls()
            {
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    try
                    {
                        //**************************************************************
                        //Create Connection String
                        //**************************************************************
                        SqlConnection sqlConn = new SqlConnection(ConnectionString);
     
                        //**************************************************************
                        // Create Chart Control
                        //**************************************************************
                        Chart barChart = new Chart();
                        barChart.Width = 412;
                        barChart.Height = 296;
     
                        //Specify palatte to use
                        barChart.Palette = ChartColorPalette.BrightPastel;
     
                        if (!String.IsNullOrEmpty(ChartName))
                        {
                            Title t = new Title(ChartName, Docking.Top, new System.Drawing.Font("Trebuchet MS", 14, System.Drawing.FontStyle.Bold), System.Drawing.Color.FromArgb(26, 59, 105));
                            barChart.Titles.Add(t);
                        }
     
                        //Create chart area
                        barChart.ChartAreas.Add("ChartArea1");
     
                        // Show as 3D
                        barChart.ChartAreas["ChartArea1"].Area3DStyle.Enable3D = true;
     
                        // Show columns as clustered
                        barChart.ChartAreas["ChartArea1"].Area3DStyle.IsClustered = false;
     
                        // Show X axis end labels
                        barChart.ChartAreas["ChartArea1"].AxisX.LabelStyle.IsEndLabelVisible = true;
     
                        // Set rotation angles
                        barChart.ChartAreas["ChartArea1"].Area3DStyle.Inclination = 15;
                        barChart.ChartAreas["ChartArea1"].Area3DStyle.Rotation = 20;
                         
                        //**************************************************************
                        // Add series data to chart 
                        //**************************************************************
     
                        //Create Series 1 if there is data
                        if (!String.IsNullOrEmpty(XColumnDataSeries1) && !String.IsNullOrEmpty(YColumnDataSeries1) && !String.IsNullOrEmpty(QuerySeries1))
                        {
                            //Add a new series
                            barChart.Series.Add("Series1");
     
                            // Set series chart type
                            barChart.Series["Series1"].ChartType = SeriesChartType.Column;
     
                            // Draw as 3D Cylinder
                            barChart.Series["Series1"]["DrawingStyle"] = "Cylinder";
     
                            barChart.Series["Series1"].BorderColor = Color.FromArgb(26, 59, 105);
                            barChart.Series["Series1"].Color = Color.CornflowerBlue;
                            barChart.Series["Series1"].BackSecondaryColor = Color.Navy;
                            barChart.Series["Series1"].BackGradientStyle = GradientStyle.DiagonalLeft;
    
                            barChart.Series["Series1"]["PointWidth"] = "0.8";
     
                            SqlCommand sqlCmdSeries1 = new SqlCommand(QuerySeries1, sqlConn);
     
                            barChart.DataSource = sqlCmdSeries1;
     
                            barChart.Series["Series1"].XValueMember = XColumnDataSeries1;
                            barChart.Series["Series1"].YValueMembers = YColumnDataSeries1;
    
                        }
     
                        barChart.DataBind();
     
                        Controls.Add(barChart);
                    }
                    catch (Exception ex)
                    {
                        Label lblError = new Label();
                        lblError.Text = "An error has occurred. Please ensure you have entered correct chart details";
     
                        Controls.Add(lblError);
                    }
                });
            }
        }
    }
    

    The code (above) provides a basis to display a database driven chart within SharePoint. With some further tweaks, your Reporting Dashboard could look something like this:

    SharePoint Report Dashboard

    If you would like the code for some of my other charts displayed within my Reporting Dashboard, please leave a comment.

  • A few weeks ago I was trying to implement a Bar and Pie Chart for a report in a web application. I found that most of the charting solutions on the web cost an arm and a leg. So I decided to have a bash at creating my own one.

    I have been reading through the MCTS Application Development Foundation book and found a couple of chapters on using System.Drawing namespace to output graphics and create Pie Charts in a C# application. Great stuff! However, I encountered a problem when my Chart was rendered within a web page that contains other HTML content. For some reason there was no HTML in my page and all that was displayed was my Chart.

    This is how I wanted my chart to be inserted into my page:

    ChartScreenshot1

    However, when my charting code was added, my page looked like this:

    ChartScreenshot2

    After investigating this problem further it seems that when you output the chart image to a stream the whole page is rendered as an image which removes all the HTML. For example:

    Response.ContentTye = "image/gif"; //MIME type
    Bitmap.Save(Response.OutputStream, ImageFormat.Gif);
    

    In order to get around this problem required quite a strange work around:

    1. In the page where you need to the chart to be displayed (we will call Report.aspx) add an ASP Image control that will link to an .aspx page that will contain your chart. Things will become more clearer in the next step.
    <asp:Image ID="imgSelfAverageBarChart" ImageUrl="/Charts/BarChart.aspx" runat="server" />
    
    1. Create a new ASP.NET page that will contain all the code for your chart (we will call BarChart.aspx). Now you might be thinking how can I send the figures to the chart? Well this can be done be using Session variables or parameters within the web page link that you used in your ImageUrl in the Report.aspx page.
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    public partial class BarChart : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                List<string> Questions = new List<string>();
                List<float> Values = new List<float>();
        
                //Check the session values have values
                if (Session["Sections"] != null && Session["SelfAverageValue"] != null)
                {
                    Questions = (List<string>)Session["Sections"];
                    Values = (List<float>)Session["SelfAverageValue"];
                }
        
                Bitmap imageBitmap = new Bitmap(600, 285);
                Graphics g = Graphics.FromImage(imageBitmap);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.Clear(Color.White);
        
                Brush[] brushes = new Brush[5];
                brushes[0] = new SolidBrush(Color.FromArgb(255, 216, 0));
                brushes[1] = new SolidBrush(Color.FromArgb(210, 219, 252));
                brushes[2] = new SolidBrush(Color.FromArgb(0, 127, 70));
                brushes[3] = new SolidBrush(Color.FromArgb(0, 148, 255));
                brushes[4] = new SolidBrush(Color.FromArgb(190, 99, 255));
        
                int xInterval = 70;
                int width = 60;
                float height = 0;
        
                //Draw the Pie Chart
                for (int i = 0; i < Values.Count; i++)
                {
                    height = (Values[i] * 40);        // adjust barchart to height of Bitmap
                    //Draws the bar chart using specific colours
                    g.FillRectangle(brushes[i], xInterval * i + 50, 260 - height, width, height);
                    //Draw legend
                    g.FillRectangle(brushes[i], 420, 25 + (i * 50), 25, 25);
                    g.DrawString(Questions[i], new Font("Arial", 8, FontStyle.Bold), Brushes.Black, 450, 31 + (i * 50));
                    // Draw the scale
                    g.DrawString(Convert.ToString(Math.Round(Convert.ToDecimal(Values[i]), 2)), 
                    new Font("Arial", 10, FontStyle.Bold), Brushes.Black, xInterval * i + 45 + (width / 3), 300 - height);
                    // Draw the axes
                    g.DrawLine(Pens.Black, 40, 10, 40, 260);        //   y-axis
                    g.DrawLine(Pens.Black, 20, 260, 400, 260);       //  x-axis
                }
        
                Response.ContentType = "image/gif";
                imageBitmap.Save(Response.OutputStream, ImageFormat.Gif);
                imageBitmap.Dispose();
                g.Dispose();
            }
            catch
            {
            }
        }
    }
    
    1. Go back to Report.aspx page and add the code to parse your values in a Session.
    //Some code that carried out calculations
    //Calculated the averages
    float selfAverageTotal = selfAssessValue / numberOfSections;
    float otherAverageTotal = otherAssessValue / numberOfSections;
    //Add generic list
    List<string> questions = new List<string>(); //To store the names of x and y axis
    List<float> averages = new List<float>();    //To store the values
    questions.Add("Self Average Total");
    averages.Add(selfAverageTotal);
    questions.Add("Other Average Total");
    averages.Add(otherAverageTotal);
    //Parse lists to session variables
    Session["Questions"] = questions;
    Session["AverageValue"] = averages;
    

    So the idea of this is that the Chart.aspx will just the render our chart and we don't care if the HTML gets wiped in this web page since we only want the image.

    You might be thinking: Why didn't you use a User Control? Well this is one of the first things I tried when trying to resolve this issue which I believe would have been a nicer implementation. Unfortunately, my report page HTML still got rendered as an image.

    If anyone knows a better way to output a chart to a webpage, then please leave a comment! Thanks!

    Oh yeah, and here is what my Bar Chart looked liked by using the above code: Sample Chart Output