Wednesday, December 10, 2008

Create a SharePoint AJAX-refreshable Web Part which wraps an ASP .NET Chart Control

Introduction

This tutorial includes the steps for making a SharePoint AJAX-refreshable Web Part which wraps an ASP .NET Chart Control.
This tutorial is the following of two previous ones:
  1. MOSS, How to...: Use Microsoft Chart Controls for .Net framework in a SharePoint web site
  2. MOSS, How to...: Use Microsoft ASP .NET Chart Control in a SharePoint Web Part
In this tutorial, we are going to put together three important ASP .NET compliant Microsoft features:
  • Windows SharePoint Services 3.0 Web parts
  • Microsoft ASP .NET Chart Control
  • Microsoft ASP .NET AJAX 3.5
Ajax integration with ASP .NET Chart Control will be easier than ever since we already had to use the .Net Framework 3.5 for the ASP .NET Chart Control, and that the most significant advance in ASP .NET with .NET Framework 3.5 is improved support for the developement of AJAX-enabled web sites.
This tutorial is compliant with both MOSS 2007 and Windows SharePoint Services 3.0.



In this SharePoint Web part Page, 4 SharePoint Web Parts embedding an ASP .NET Chart Control can be refreshed independently some of the others using ASP .NET AJAX 3.5 and can be used to build a SharePoint Dashboard.

1 - Prerequisites

2 - Important ASP .NET AJAX 3.5 information:

1 - Using UpdatePanel control and efficiency.

There are roughly two ways of doing Ajax with Microsoft Ajax Framework.
  • Using UpdatePanel control
  • Using a web Service. ( ASMX Web method called through the javaScript Proxy).
Using UpdatePanel control while adding AJAX functionality to an aspx page DOES NOT increase performances of the page since when UpdatePanel control performs an asynchronous AJAX callback to the server to update its content, the request contains everything a conventional ASP .NET postback contains, including view state.
Unfortunatly, as an ASP .NET Chart Control is a Server Conrol, the only (easy) way of adding AJAX improvements to an .aspx page regarding an ASP .NET Chart Control is to use Update Panel. Of course Best Practices in using UpdatePanel can increase the page efficiency.

For more information there is this article of Jeff Prosise in MSDN Magazine (june 2007 issue)

2 - URL of the hosting SharePoint Web site

An article of Microsoft Knowledge Base reports that issue may occur if you use international characters in an URL of a SharePoint Page that contains a Web Part with ASP .NET AJAX Functionality. It is possible that this bug doesn't exist anymore with the ASP .NET AJAX 3.5 version.
For more information:

3 - Tutorial Overview

This tutorial includes the following steps:
  • Step 1: Configure your SharePoint environment for ASP .NET Ajax 3.5
  • Step 2: Create a new Class in AspNetChartControl.SharePointWebParts project
  • Step 3: Give the Class the Basic Structure for a SharePoint Web Part without forgetting the "public" class declaration.
  • Step 4: Define the Logic and rendering of your Web Part
  • Step 5: Implement Ajax functionality
  • Step 6: Build your web part
  • Step 7: Update the DLL already present in the web Application Bin Directory
  • Step 8: Import the site template default.aspx page in Visual Studio and rename it
  • Step 9: Add a Script Manager to the Web Part Page defaultWithScriptManager.aspx
  • Step 10: Add new Web Part Zones to the Web Part Page defaultWithScriptManager.aspx
  • Step 11: Import the Web Part Page defaultWithScriptManager.aspx into your Sharepoint Web Site.
  • Step 12: Import several Web Parts AjaxRefreshableChartWebPart into your Web Part Page.

4 - Tutorial

4.1 - Step 1: Configure your SharePoint environment for ASP .NET Ajax 3.5

We are going to add entries in the web.config file of the Web Application where is running the SharePoint Team site that is planned to welcome the Ajax-Refreshable Web part.
There is several way to modify this file in order to add support for .Net 3.5 in the SharePoint Web Application:
And the manual way that I have chosen to show here in its minimal version, in order to modify the web.config as little as possible since we have modified it yet for supporting ASP .NET Chart Control, and we have to use caution when modifying a Web Application web.config that is already complex enough.
Here are the steps (I have mentionned all of them ordered as in a SharePoint web.config file, but also mentionned the fact I have skipped some):
  • <config section> (skipped)
  • <SafeControls>
  • <httpHandlers>
  • <httpModules>
  • <assemblies>
  • <controls>
  • <system.web.extension> (skipped)
  • <system.web.server> (skipped)

  • <SafeControls>
    <!-- start adding -->
          <SafeControl Assembly="System.Web.Extensions, version=3.5.0.0, culture=Neutral, publicKeyToken=31bf3856ad364e35" Namespace ="System.Web.UI" TypeName="*" Safe="True" />
    <!-- end adding -->
    </SafeControls>
    
  • <httpHandlers>
          <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false" />
    
  • <httpModules>
          <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  • <assemblies>
            <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
            <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
            <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
            <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
    
    
  • <controls>
          </tagMapping>
          <!-- start adding -->
          <controls>
            <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add tagPrefix="asp" namespace="System.Web.UI.DataVisualization.Charting" assembly="System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
          </controls>
          <!-- end adding -->
        </pages>
    

4.2 - Step 2: Create a new Class in AspNetChartControl.SharePointWebParts project

To create a new class
  1. Right click your project in the Solution Explorer
  2. Select Add in the first menu
  3. Select Class in the second menu
  4. In the opening Dialog type the new Class name: AjaxRefreshableWebPart.cs
  5. Click Add.





This is the code state at the end of this step:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AspNetChartControl.SharePointWebParts
{
    class AjaxRefreshableWebPart
    {
    }
}

4.3 - Step 3: Give the Class the Basic Structure for a SharePoint Web Part without forgetting the "public" class declaration.

Refering to previous tutorial from 3.2 to 3.5, give the Class the basic structure for a Sharepoint Web part Class.
This is the code state at the end of this step:
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;
using System.Xml;
using System.Diagnostics;

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


        protected override void CreateChildControls()
        {
            base.CreateChildControls();
        }

        protected override void RenderWebPart(HtmlTextWriter output)
        {
            base.RenderWebPart(output);
        }
    }
}

4.4 - Step 4: Define the Logic and rendering of your Web Part

We are now using again one of the ASP .NET Chart Control Sample. In one of these samples (Stacked Charts), there is a piece of C# code that shows how to generate a random Chart Serie for a Chart Control. As we have not yet bound our Chart Control to a Data Source, we will use this random Chart Serie generation to simulate a real Data Source binding.



We have also to implement an ASP LinkButton to give user the chance to refresh the Web Part using it.
At the end of this step, we have a perfectly well working Wep Part, but it has not yet AJAX ability. That is to mean, if we build this code and deploy the Web Part, the click on the LinkButton will trigger a visible Postback of the Web Part Page.
This is the code state at the end of this step:
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;
using System.Xml;
using System.Diagnostics;

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

        System.Web.UI.DataVisualization.Charting.Chart Chart1;
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            LinkButton button1 = new LinkButton();
            button1.ID = "lButton1";
            button1.Text = "Refresh";
            button1.Click += new EventHandler(lButton_Click);

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

            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("Ajax-Refreshable ASP.NET Chart Control Web Part", 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.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
            Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
            Chart1.BorderWidth = 2;

            if (Chart1.Series.FindByName("Series1") == null)
            {
                Chart1.Series.Add(new Series("Series1"));
                Chart1.ChartAreas.Add("Series1");
            }

            Random random = new Random();

            for (int pointIndex = 0; pointIndex < 10; pointIndex++)
            {
                Chart1.Series["Series1"].Points.AddY(random.Next(45, 95));
            }

            this.Controls.Add(Chart1);
            this.Controls.Add(button1);
        }

        void lButton_Click(object sender, EventArgs e)
        {
            Random random = new Random();

            for (int pointIndex = 0; pointIndex < 10; pointIndex++)
            {
                Chart1.Series["Series1"].Points.AddY(random.Next(45, 95));
            }
        }

        protected override void RenderWebPart(HtmlTextWriter output)
        {
            base.RenderWebPart(output);
        }
    }
}

4.5 - Step 5: Implement Ajax functionality

We are now going to give the Web Part its AJAX ability to be refreshed without triggering a visible Post Back. We know now that unfortunatly for the Page efficiency, UpdatePanel control trigger a PostBack, even if it is no visible.
For people that does not know at all how to use an UpdatePanel control, let's show the declarative syntax for that control that is very simple.
To use an UpdatePanel control in an ASP .NET Page and to give a piece of code the AJAX ability you just have to write that:
    <asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="true" runat="server">
    </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1"  runat="server">
           <ContentTemplate>
              <!-- Place your code here -->
           </ContentTemplate>
        </asp:UpdatePanel>

Here is the previous code decorated with the implementation of the AJAX feature using the imperative syntax:
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;
using System.Xml;
using System.Diagnostics;

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

        System.Web.UI.DataVisualization.Charting.Chart Chart1;
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            
            //UpdatePanel declaration, instantiation and identification
            UpdatePanel updatePanel1 = new UpdatePanel();
            updatePanel1.ID = "udpAjaxRefreshableChartControlWebPart";
            //the way the of refreshing
            updatePanel1.UpdateMode = UpdatePanelUpdateMode.Conditional;

            LinkButton button1 = new LinkButton();
            button1.ID = "lButton1";
            button1.Text = "Refresh";
            button1.Click += new EventHandler(lButton_Click);

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

            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("Ajax-Refreshable ASP.Net Chart Control Web Part", 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.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
            Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
            Chart1.BorderWidth = 2;

            if (Chart1.Series.FindByName("Series1") == null)
            {
                Chart1.Series.Add(new Series("Series1"));
                Chart1.ChartAreas.Add("Series 1");

            }

            Random random = new Random();
            Debug.WriteLine("AjaxRefreshWebPart - CreateChildControls");


            for (int pointIndex = 0; pointIndex < 10; pointIndex++)
            {
                Chart1.Series["Series1"].Points.AddY(random.Next(45, 95));
            }

            //the controls are embedded inside the UpdatePanelControl
            updatePanel1.ContentTemplateContainer.Controls.Add(Chart1);
            updatePanel1.ContentTemplateContainer.Controls.Add(button1);

            this.Controls.Add(updatePanel1);
        }

        void lButton_Click(object sender, EventArgs e)
        {
            Random random = new Random();

            for (int pointIndex = 0; pointIndex < 10; pointIndex++)
            {
                Chart1.Series["Series1"].Points.AddY(random.Next(45, 95));
            }
        }

        protected override void RenderWebPart(HtmlTextWriter output)
        {
            base.RenderWebPart(output);
        }
    }
}

4.6 - Step 6 : 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.

4.7 - Step 7: Update the DLL already present in the web Application 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. Replace the AspNetChartControl.SharePointWebParts.dll in the the Web application root bin directory with the file from the output directory.

4.8 - Step 8: Import the site template default.aspx page in Visual Studio and rename it

Navigate to the following directory
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\sts
Copy the default.aspx page and paste it in your project directory.
In the Visual Studio Solution Explorer click Display All Files button to make the default.aspx page to be visible.
Right click the page and click on "Include In Project".
Rename the page in "defaultWithScriptManager.aspx"



4.9 - Step 9: Add a Script Manager to the Web Part Page defaultWithScriptManager.aspx

open the Page in Code view
Expand Visual Studio Toolbox and locate the ScriptManager control and drag it.



Locate the first WepPartZone declaration and drop a ScriptManager instance upon it.




4.10 - Step 10: Add new Web Part Zones to the Web Part Page defaultWithScriptManager.aspx

Locate the HTML <tr> line that contains the 2 webPartZone and duplicate it.



Rename the 4 WebPartZones in order to obtain the following HTML code:
         <table width="100%" cellpadding=0 cellspacing=0 style="padding: 5px 10px 10px 10px;">
          <tr>
           <td valign="top" width="70%">
               <asp:ScriptManager ID="ScriptManager1" runat="server">
               </asp:ScriptManager>
               <WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly" ID="LeftUp" Title="loc:LeftUp" />
               &nbsp;
           </td>
           <td>&nbsp;</td>
           <td valign="top" width="30%">
               <WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly" ID="RightUp" Title="loc:RightUp" />
               &nbsp;
           </td>
           <td>&nbsp;</td>
          </tr>
                    <tr>
           <td valign="top" width="70%">
               <WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly" ID="LeftDown" Title="loc:LeftDown" />
               &nbsp;
           </td>
           <td>&nbsp;</td>
           <td valign="top" width="30%">
               <WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly" ID="RightDown" Title="loc:RightDown" />
               &nbsp;
           </td>
           <td>&nbsp;</td>
          </tr>
         </table>
When the changes are done, save the page.

4.11 - Step 11: Import the Web Part Page defaultWithScriptManager.aspx into your Sharepoint Web Site.

Go to the Shared Documents document library of your team site and select upload a document.


Then navigate to your project directory and choose the defaultWithScriptManager.aspx page.





4.12 - Step 12: Import several Web Parts AjaxRefreshableWebPart into your Web Part page.

Now you can add 4 AjaxRefeshableChartControlWebParts in your Page.



And notice that you can rename them, and they can be refreshed independently some of the others using ASP .NET AJAX 3.5 and can be used to build a SharePoint Dashboard.



5 - For more information

You can find additional information at: I plan writing soon a post on Data Binding for these Web Parts, and a more generic article on dashboards with WSS 3.0.
By the way, there will be an interesting conference on that topic in february:
Case Studies in SharePoint Dashboards - The Sequel
If you are planning to realize such dashboards using ASP .NET Chart Controls, I will be pleased to exchange information with you...

5 comments:

Anonymous said...

Absolutely the star!!

Give this man a job, can't wait for the next installment of this thrilling development, the Data Binding part...

Only thing is - I'm not a C# guy, I prefer VB...

Still beggers can't be choosers, or can they?

Suppose I have to learn c#...

regards
Sean

Marc Charmois said...

Hi sean,

thank you for your comment. I am very touched.

I have just tried this C# to VB converter, and it seems to be good.

http://www.developerfusion.com/tools/convert/csharp-to-vb/

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports Microsoft.SharePoint.WebPartPages
Imports System.Web.UI.DataVisualization.Charting
Imports System.Xml
Imports System.Diagnostics

Namespace AspNetChartControl.SharePointWebParts
<ToolboxData("<{0}:AjaxRefreshableWebPart runat=server></{0}:AjaxRefreshableWebPart>")> _
Public Class AjaxRefreshableWebPart
Inherits Microsoft.SharePoint.WebPartPages.WebPart

Private Chart1 As System.Web.UI.DataVisualization.Charting.Chart
Protected Overloads Overrides Sub CreateChildControls()
MyBase.CreateChildControls()

'UpdatePanel declaration, instantiation and identification
Dim updatePanel1 As New UpdatePanel()
updatePanel1.ID = "udpAjaxRefreshableChartControlWebPart"
'the way the of refreshing
updatePanel1.UpdateMode = UpdatePanelUpdateMode.Conditional

Dim button1 As New LinkButton()
button1.ID = "lButton1"
button1.Text = "Refresh"
AddHandler button1.Click, AddressOf lButton_Click

Chart1 = New System.Web.UI.DataVisualization.Charting.Chart()

Chart1.Width = 412
Chart1.Height = 296
Chart1.RenderType = RenderType.ImageTag
Dim imagespath As String = System.Configuration.ConfigurationSettings.AppSettings("ChartImageHandler").ToString()
Chart1.ImageLocation = imagespath & "ChartPic_#SEQ(200,30)"

Chart1.Palette = ChartColorPalette.BrightPastel
Dim t As New Title("Ajax-Refreshable ASP.Net Chart Control Web Part", 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.BorderSkin.SkinStyle = BorderSkinStyle.Emboss
Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105)
Chart1.BorderlineDashStyle = ChartDashStyle.Solid
Chart1.BorderWidth = 2

If Chart1.Series.FindByName("Series1") Is Nothing Then
Chart1.Series.Add(New Series("Series1"))

Chart1.ChartAreas.Add("Series 1")
End If

Dim random As New Random()
Debug.WriteLine("AjaxRefreshWebPart - CreateChildControls")


For pointIndex As Integer = 0 To 9
Chart1.Series("Series1").Points.AddY(random.[Next](45, 95))
Next

'the controls are embedded inside the UpdatePanelControl
updatePanel1.ContentTemplateContainer.Controls.Add(Chart1)
updatePanel1.ContentTemplateContainer.Controls.Add(button1)

Me.Controls.Add(updatePanel1)
End Sub

Private Sub lButton_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim random As New Random()

For pointIndex As Integer = 0 To 9
Chart1.Series("Series1").Points.AddY(random.[Next](45, 95))
Next
End Sub

Protected Overloads Overrides Sub RenderWebPart(ByVal output As HtmlTextWriter)
MyBase.RenderWebPart(output)
End Sub
End Class
End Namespace

Hope this helps.

Marc

Unknown said...

Well, it only gets better...

I never knew about this C# to VB converter, so will be trying it out. Thanks again.

Only thing I forgot to ask was, and I know all about deadlines, the future etc. when do you think the Data Binding follow up will be available for review?

regards,
Sean

Marc Charmois said...

Hi Sean,

I was planning doing it within this week, since after having worked several (long) weeks on Reporting Services, I am back with the Asp .Net Charting Control embedding web part development.

So you should have something about it very soon... ;-)

Marc

Marc Charmois said...

Hi sean,

as promissed, within the week (it was my last day yesterday... ;-)) I have posted something on the ASP .NET Chart Control Binding within a SharePoint Web Part.

Bind a Microsoft ASP .NET Chart Control within a SharePoint web part to a DataSetHope this helps.


Marc