Thursday, December 4, 2008

Use a Microsoft Asp .Net Chart Control in a SharePoint Web Part

Introduction

This tutorial includes the steps for creating a basic custom Windows SharePoint Services Web Part which wraps a Microsoft Chart Control for ASP .Net. It is a simple Web Part that allows you to change the Enabled property of the ASP .Net Chart control's legend. The System.Web.UI.DataVisualization.Charting.Legend class is used to manage the legend of the Asp .Net Chart.
The goal of this tutorial was to place inside a Web Part the "Chart without Code-Behind" sample, that is one of the 200 Interactive Samples for Asp .Net Chart Control from Samples Environment for Microsoft Chart Controls .



This also, shows how to develop quickly : test your solution in "in line" script, then, deploy it using a component, as I wrote before in This tutorial is compliant with both MOSS 2007 and Windows SharePoint Services.

Beginning with Windows SharePoint Services 3.0, the Windows SharePoint Services Web Part infrastructure is built on top of the Microsoft ASP.NET 2.0 Web Part infrastructure and Web Parts that derive from the ASP.NET WebPart class are completely supported in Windows SharePoint Services. You should create ASP.NET Web Parts whenever possible.
For more information about choosing the best Web Part base class from which to derive, see Developing Web Parts in Windows SharePoint Services in the Windows SharePoint Services Software Development Kit (SDK). For more information about ASP.NET Web Parts, see the Web Parts Control Set Overview in the ASP.NET documentation.

To see a much more complete example of a Web Part wrapping an ASP .net Chart Control, you can go to CodePlex and examine the Wictor Wilén's ChartPart derive from ASP .Net WebPart class. The one of this tutorial derive from Windows SharePoint Services Web Part Class.

1 - Prerequisites

  • Usual development environment for SharePoint (Windows 2003 Server or Windows Server 2008 with a SharePoint installation).
  • Microsoft Chart Controls for Microsoft .NET Framework 3.5 are installed on the development environment. You can download installation package here
  • Microsoft Visual Studio 2008 is installed on the development environment.
  • Microsoft Chart Controls Add-on for Microsoft Visual Studio 2008 is installed on the development environment.
  • Samples Environment for Microsoft Asp .Net Chart Controls is installed on the development environment.
  • Your SharePoint environement is properly configured for ASP .Net Chart Controls. If you are not sure you can check the configuration in my previous post, sections 3.3 and 3.4, or do this previous post quick tutorial in order to validate your environment by displaying the Chart Control Sample on a SharePoint page.
  • You have a SharePoint team site ready to welcome the Asp .Net Chart control Web part.

2 - Tutorial Overview

This tutorial includes the following steps:
  • Step 1: Create an ASP .Net Server Control project
  • Step 2: Add a reference to Microsoft.SharePoint.dll, System.Web.DataVisualization.dll and regarding Namespace Directives
  • Step 3: Rename the Class
  • Step 4: Inherit from the Web part Class
  • Step 5: Use the RenderWebpartMethod
  • Step 6: Define the Logic and rendering of your Web Part using Asp .Net Chart Control Samples and the CreateChildControls Method
  • Step 7: Build your web part
  • Step 8: Copy your DLL to the Bin Directory
  • Step 9: Add ControlSafe Entry in the Web Application web.config
  • Step 10: Import the site template default.aspx page and use it as a Web Part page
  • Step 11: Import the ASP .Net Chart Control Web Part into your Web Part page.
Warning: I skipped some steps in order to give a quick result. To be really compliant with the SharePoint Web Part development best practices refer to:

3 - Tutorial

3.1 Step 1: Create an ASP .Net Server Control project
To create a new ASP .Net Server Control project
  1. Start Visual Studio 2008.
  2. On the File menu, point to New, and then click Project.
  3. In the New Project dialog box, click Visual C# Projects or Visual Basic Projects, and then select the ASP .Net Server Control project template.
  4. Type AspNetChartControl.SharePointWebParts as the name and specify the location for the project files, and then click OK.




3.2 Step 2: Add a reference to Microsoft.SharePoint.dll, System.Web.DataVisualization.dll and regarding Namespace Directives
To create a Web Part, you need to add a reference to the Microsoft.SharePoint assembly (Microsoft.SharePoint.dll) in your Web Control Library project.
To add a reference to Microsoft.SharePoint.dll
  1. On the Project menu, click Add Reference.
  2. On the .NET tab, double-click Windows SharePoint Services.
  3. Click OK.



To make it easier to write a basic WebPart class, you should use the using directive in C# or the Imports directive in Visual Basic to reference the following namespace in your code:
The Microsoft.SharePoint.WebPartPages namespace provides the types for Web Part pages, such as the Microsoft.SharePoint.WebPartPages.WebPart class.



As we are going to use a Microsoft Asp .Net Chart Control in our web part, we have to do exactly the same for the System.Web.DataVisualization.dll and regarding System.Web.UI.DataVisualization.Charting namespace.





3.3 Step 3: Rename the Class and clean up the code
To rename the class
  1. In the solution explorer right click the class file and select rename
  2. Rename the file in DynamicLegendWebPart (don't forget extension)
  3. Click ok on the message box that will ask you to rename the other instance of the clas name inside your source code.
Don't forget to rename the parameter of the ToolBoxData Attribute.
As we won't use any property for this web part, delete the "Text" property, the corresponding class attribute and the reference in the Render Content Method.



This is the code state at the end of this step:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace AspNetChartControl.SharePointWebParts
{
    [ToolboxData("<{0}:DynamicLegendWebPart runat=server></{0}:DynamicLegendWebPart>")]
    public class DynamicLegendWebPart : WebControl
    {


        protected override void RenderContents(HtmlTextWriter output)
        {

        }
    }
}
3.4 Step 4: Inherit from the Web part Class
By default, the Web Control Library template creates a custom control that inherits from the System.Web.UI.Control class (a parent class of both the ASP.NET and SharePoint WebPart classes).
To create a SharePoint Web Part, you should inherit from the Microsoft.SharePoint.WebPartPages.WebPart base class.

To inherit from the Web Part base class.

Replace this line of code:
public class DynamicLegendWebPart : WebControl 
with this line:
public class DynamicLegendWebPart : WebPart
This is the code state at the end of this step:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace AspNetChartControl.SharePointWebParts
{
    [ToolboxData("<{0}:DynamicLegendWebPart runat=server></{0}:DynamicLegendWebPart>")]
    public class DynamicLegendWebPart : WebPart
    {


        protected override void RenderContents(HtmlTextWriter output)
        {

        }
    }
}

3.5 Step 5: Use the RenderWebpartMethod
The WebPart base class seals the Render method of System.Web.UI.Control because the Web Part infrastructure needs to control rendering the contents of a Web Part. For this reason, custom Web Parts must override the RenderWebPart method of the WebPart base class.
To use the RenderWebPart method.
Replace this line of code:

protected override void RenderContents(HtmlTextWriter output)
with this line:
protected override void   RenderWebPart(HtmlTextWriter output)
3.6 Step 6: Define the Logic and rendering of your Web Part using Asp .Net Chart Control Samples and the CreateChildControls Method


After you complete the previous steps, you can define the logic and rendering for your Web Part. For this part, we will, as annouced previously, rewrite the in-line code of the "Chart Without Code Behind" Sample, in order to make this sample work inside a web part.



The steps are the following :
  1. Write a CreateChildControls Method to add the Asp .Net Chart Control and the CheckBox Control as child controls of the Web Part
  2. Inside this method:
    • add text and Autopostback to the CheckBox
    • Change the image location definition using our web.config AppSettings
    • Change the Chart Title
    • Add the controls as children
  3. Move the render instruction to the RenderWebPart Method
  4. Override OnLoad Method in order to see the changes after the PostBack
  5. Place the Legend Enabling instructions inside the OnLoad Method
  6. Move the declaration and instanciation instructions for the two child controls outside the methods in order to factorize them.


Here is the complete code before rewriting with the TODO instructions:
  <%
                        //put this at the top of the class to this to be shared by all the methods
                        System.Web.UI.DataVisualization.Charting.Chart Chart1 = new System.Web.UI.DataVisualization.Charting.Chart();
                        //declare and instantiate the checkBox control in the same place and way
                        
                        //All the following (untill next method instruction) go Inside CreateChildControls Method
                        //Set the text property value of the Checkbox control to "Show Legend"
                        //Set the AutoPostBack porperty value to true
                        
                        Chart1.Width = 412;
                        Chart1.Height = 296;
                        Chart1.RenderType = RenderType.ImageTag;
                        //Use the AppSettings to retrieve the path from our web.config
                        Chart1.ImageLocation = "..\\..\\TempImages\\ChartPic_#SEQ(200,30)";
                        
                        Chart1.Palette = ChartColorPalette.BrightPastel;
                        //Change the title to " Dynamic Legend SharePoint Web Part with ASP .net Chart Control"
                        Title t = new Title("No Code Behind Page", Docking.Top, new System.Drawing.Font("Trebuchet MS", 14, System.Drawing.FontStyle.Bold), System.Drawing.Color.FromArgb(26, 59, 105));
                        Chart1.Titles.Add(t);
                        Chart1.ChartAreas.Add("Series 1");
                        // create a couple of series
                        Chart1.Series.Add("Series 1");
                        Chart1.Series.Add("Series 2");
                        
                        
                        // add points to series 1
                        Chart1.Series["Series 1"].Points.AddY(5);
                        Chart1.Series["Series 1"].Points.AddY(8);
                        Chart1.Series["Series 1"].Points.AddY(12);
                        Chart1.Series["Series 1"].Points.AddY(6);
                        Chart1.Series["Series 1"].Points.AddY(9);
                        Chart1.Series["Series 1"].Points.AddY(4);
                         
                        // add points to series 2
                        Chart1.Series["Series 2"].Points.AddY(2);
                        Chart1.Series["Series 2"].Points.AddY(6);
                        Chart1.Series["Series 2"].Points.AddY(18);
                        Chart1.Series["Series 2"].Points.AddY(16);
                        Chart1.Series["Series 2"].Points.AddY(21);
                        Chart1.Series["Series 2"].Points.AddY(14);
                          
                        Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
                        Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
                        Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
                        Chart1.BorderWidth = 2;
                        
                        Chart1.Legends.Add("Legend1");
                        
                            // show legend based on check box value
                        //Put this in OnLoad Method 
                        Chart1.Legends["Legend1"].Enabled = ShowLegend.Checked;
                        //Put this in the RenderWebPart Method with the correct syntax
                        // Render chart control
                        Chart1.Page = this;
                        HtmlTextWriter writer = new HtmlTextWriter(Page.Response.Output);
                        Chart1.RenderControl(writer);
                    
                     %>
                     <!-- Make a child control of the web part with all the following -->
                    </td>
                    <td valign="top">
                        <table class="controls" cellpadding="4">
                            <tr>
                                <td class="label48"></td>
                                <td>
                                    <p><asp:checkbox id="ShowLegend" runat="server" Text="Show Legend" AutoPostBack="True"></asp:checkbox></p>
                                </td>
                            </tr>
                        </table>

Here is the complete code after rewriting: DynamicLegendWebpart.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.DataVisualization.Charting;
namespace AspNetChartControl.SharePointWebParts
{
    [ToolboxData("<{0}:DynamicLegendWebPart runat=server></{0}:DynamicLegendWebPart>")]
    public class DynamicLegendWebPart : WebPart
    {
        System.Web.UI.DataVisualization.Charting.Chart Chart1 = new System.Web.UI.DataVisualization.Charting.Chart();
        CheckBox ShowLegend = new CheckBox();
        protected override void OnLoad(EventArgs e)
        {
            Chart1.Legends.Add("Legend1");
            Chart1.Legends["Legend1"].Enabled = ShowLegend.Checked;
        }
        protected override void CreateChildControls()
        {
            ShowLegend.AutoPostBack = true;
            ShowLegend.Text = "Show Legend ";
            Chart1.Width = 412;
            Chart1.Height = 296;
            Chart1.RenderType = RenderType.ImageTag;
            string imagespath = System.Configuration.ConfigurationSettings.AppSettings["ChartImageHandler"].ToString();
            Chart1.ImageLocation = imagespath + "ChartPic_#SEQ(200,30)";
            Chart1.Palette = ChartColorPalette.BrightPastel;
            Title t = new Title("Dynamic Legend Web Part with ASP .Net Chart Control", Docking.Top, new System.Drawing.Font("Trebuchet MS", 14, System.Drawing.FontStyle.Bold), System.Drawing.Color.FromArgb(26, 59, 105));
            Chart1.Titles.Add(t);
            Chart1.ChartAreas.Add("Series 1");
            // create a couple of series
            Chart1.Series.Add("Series 1");
            Chart1.Series.Add("Series 2");
            // add points to series 1
            Chart1.Series["Series 1"].Points.AddY(5);
            Chart1.Series["Series 1"].Points.AddY(8);
            Chart1.Series["Series 1"].Points.AddY(12);
            Chart1.Series["Series 1"].Points.AddY(6);
            Chart1.Series["Series 1"].Points.AddY(9);
            Chart1.Series["Series 1"].Points.AddY(4);
            // add points to series 2
            Chart1.Series["Series 2"].Points.AddY(2);
            Chart1.Series["Series 2"].Points.AddY(6);
            Chart1.Series["Series 2"].Points.AddY(18);
            Chart1.Series["Series 2"].Points.AddY(16);
            Chart1.Series["Series 2"].Points.AddY(21);
            Chart1.Series["Series 2"].Points.AddY(14);
            Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
            Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
            Chart1.BorderWidth = 2;
            this.Controls.Add(Chart1);
            this.Controls.Add(ShowLegend);
        }
        protected override void RenderWebPart(HtmlTextWriter output)
        {
            // Render chart control and checkBox
            RenderChildren(output);
        }
    }
}
3.7 Step 7: Build your web part
After you add all of the preceding code, you can build your sample Web Part.

To build your Web Part
On the Build menu, click Build Solution.
3.8 Step 8: Copy your DLL to the Bin Directory
After the Web Part is built, you must copy the resulting DLL to the bin directory. To copy your DLL to the bin directory
  1. On the file system, locate the AspNetChartControl.SharePointWebparts.dll file. It should be in
    C:\AspNetChartControl.SharePointWebparts\AspNetChartControl.SharePointWebparts\bin\Debug.
  2. Copy the AspNetChartControl.SharePointWebParts.dll file from the output directory to the Web application root bin directory. The default location of the Web application root for is C:\Inetpub\wwwroot\wss\VirtualDirectories\PortNumber\bin.




3.9 Step 9: Add ControlSafe Entry in the Web Application web.config
I skipped some steps required to be compliant with the best practices, for the demonstration to be quick. But you should usually:
  • compile the dll with a strong name and an AllowPartiallyTrustedCallers attribute
  • at least modify the trust level of the web.config and the best would be to use Code Access Security policies
.Anyway for a fast demonstration, with just adding the non-signed dll in the bin directory and this entry in the web.config, it works !
 <SafeControl Assembly="AspNetChartControl.SharePointWebParts" Namespace="AspNetChartControl.SharePointWebParts" TypeName="*" Safe="true" />
<SafeControls />
3.10 Step 10: Import the site template default.aspx page and use it as a Web Part page
I want now to show an amazing trick to have quickly a web Part Page with a master page, especially for people who just have WSS 3.0.
Go to the Shared Documents document library of your team site and select upload a document.



Then navigate to the following directory
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\sts
and choose the default.aspx page.



You have now in the Shared Documents library a brand new Web Part Page with the Quick Launch menu !



3.11 Step 11: Import the ASP .Net Chart Control Web Part into your Web Part page.


To import your Web Part

Navigate to the site collection settings of your site, on the Site Settings page, click Web Parts under the Galleries heading.




In the Web Part Gallery, click New.
Locate and check AspNetChartControl.SharePointWebParts.DynamicLegendWebPart



Click on button Populate Gallery

The Web part is now available in the Web part gallery



Navigate back to the Web Part page. In the Web Part page, click Site Actions, and select Edit Page.

In your preferred zone, click Add a Web Part and check the box next to DynamicLegendWebPart in the dialog box. Click Add.





After the Web Part is added to the zone, check and uncheck the Show Legend CheckBox to test the Web Part.





11 comments:

Anonymous said...

Hi Marc,
I'm trying your procedure..I'm getting
(The "DynamicLegendWebPart" Web Part appears to be causing a problem. Invalid 'url' in chart handler configuration. ) when adding the webpart to the page.

Where's this url defined?
Thanks..

Anonymous said...

Hi,
Solved the case ..
the url is the one given in web.config in line:
add key="ChartImageHandler" value="storage=memory;timeout=20;URL=/_layouts/Images/MicrosoftChartControls/"

Thanks..

Leo said...

Hi Marc, i have created Sharepoint site with MSChart Controll following your instructions, but i can't cautch Post_Paint Event from ChartArea. Do i need make some changes in webconfig to cautch this event?

1000 Thanks!

Marc Charmois said...

Hi Leo,

Sorry, I have just seen your message today. I will check and try to find an answer as soon as possible.

Anonymous said...

Hi,
I got error that i couldnot add my web parts bcoz i couldnot import it into my site.

Anonymous said...

hI,

Bit of background I am using the MSChart Controls in a sharepoint environment that must be deployable on a farm environment.

I am using the following url parameter to try and store the temporary images in a sharepoint:



where /Images1/ is the relative url of the document library. i.e. the full url is http://localhost:80/Images1/.

However, I get the following error:

An exception occurred:System.InvalidOperationException: Invalid 'url' in chart handler configuration. ---> System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet) at System.Security.CodeAccessPermission.Demand() at System.Web.HttpRequest.MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, Boolean allowCrossAppMapping) at System.Web.HttpServerUtility.MapPath(String path) at System.Web.UI.DataVisualization.Charting.ChartHttpHandlerSettings.Inspect() The action that failed was: Demand The type of the first permission that failed was: System.Security.Permissions.FileIOPermission The Zone of the assembly that failed was: MyComputer --- End of inner exception stack trace --- at System.Web.UI.DataVisualization.Charting.ChartHttpHandlerSettings.Inspect() at System.Web.UI.DataVisualization.Charting.ChartHttpHandlerSettings.ParseParams(String parameters) at System.Web.UI.DataVisualization.Charting.ChartHttpHandlerSettings..ctor(String parameters) at System.Web.UI.DataVisualization.Charting.ChartHttpHandler.InitializeParameters() at System.Web.UI.DataVisualization.Charting.ChartHttpHandler.EnsureInitialized(Boolean hardCheck) at System.Web.UI.DataVisualization.Charting.Chart.GetImageStorageMode() at System.Web.UI.DataVisualization.Charting.Chart.Render(HtmlTextWriter writer) at ChartPart.ChartPartWebPart.Render(HtmlTextWriter writer)

The account of my app pool has full write access to the site and to the content database.

Any Ideas why I may be getting this error?

Thanks in advance.

Anonymous said...

Dohh. My fault should have checked the trust levels first. Increased them to High or WSS_Medium and puff I have a chart.

Does anyone know the recommended place to store the temporary image files for farm deployment? Say mutliple front end webs.

If I store it memory it will be local to the box and the second http request to get the image may fail if it goes to a different server. The only way I could think of would be a virtual path where the directory is on shared network space. But I would prefer to use the database. Would this require a custom http handler?

Thanks,
Karl.

Marc Charmois said...

Hi,

you should rather use an httpModule.

Here is a complete example of the use of httpModule for virtual paths :

http://blogs.msdn.com/b/gzunino/archive/2007/09/17/hosting-a-wcf-service-in-windows-sharepoint-services-v3-0.aspx

Cheers!

Marc

Anonymous said...

Hi Marc,

I'm new to both SharePoint and MS Charts. My first project that I need to create involves both. I followed your step with 'Use a Microsoft Asp .Net Chart Control in a SharePoint Web Part' very carefully. Once I uploaded it to Shartpoint, I got the error message:
Object reference not set to an instance of an object. at AspNetChartControl.SharePoint.WebParts.DynamicLegendWebPart.CreateChildControls()

Do I need to create an additional file within the solution other than 'DynamicLegendWebPart.cs' as you described? Would you please steer me in the right direction? I'm hoping that once I can get your example working, I can use it as a guideline for the charts I need to development.

Thanks!

Anonymous said...

Hi Marc,

I found out what happened. In your other tutorial (http://mosshowto.blogspot.com/2008/11/chart-controls-net-framework-sharepoint.html), it had more entries within the web.config file. Once I put those entries in, the web part appeared within my sharepoint page.

Thanks for your tutorials! They are very helpful.

TARUN said...

how can we generate chart dynamically. i.e I have a drop down list with department details. When i select the deparment name .the coressponding department details should me displayed on the chartcontrols....
Anyone help me my mail id is
tarun00198@gmail.com
Thanks in advance..