Monday, February 2, 2009

Adding AJAX Update Panel to SharePoint User Controls

I wanted to add an update panel to any WebPart (User Control) we needed it on.

I had to pull together a few posts/articles to make this work, but the two main ones come from:
http://sharepoint.microsoft.com/blogs/mike/Lists/Posts/Post.aspx?ID=3
http://www.asp.net/AJAX/documentation/live/Tutorials/UsingUpdatePanelControls.aspx

So first you need to get AJAX running on the system and set up in SharePoint.

To extend your SharePoint site with Microsoft ASP.NET AJAX 1.0, you’ll need to perform a few steps.

First, you will need to download and install ASP.NET AJAX on servers in your farm.

Second, you need to extend web.config with some settings to enable ASP.NET AJAX technology.

Installing ASP.NET AJAX on servers in your farm

You will want to install the full "ASP.NET 2.0 AJAX Extensions 1.0" from ajax.asp.net.

Extending SharePoint web.config files with Microsoft ASP.NET AJAX 1.0

Extending SharePoint web.config files with ASP.NET AJAX requires that you interleave some Ajax registration entries in-line with WSS registration entries. To do this you will need to edit your SharePoint web.config file, typically in a directory like c:\inetpub\wwwroot\wss\virtualdirectories\80.

1. Add a <sectionGroup>element to the <configSections>tag:
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere" />
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>



2. Add a <controls> section as a child of the <system.web>/<pages> tag.
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</controls>
</pages>



3. Add the following tag to the <assemblies> tag, within <compilation>:
<assemblies>
<add assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</assemblies>



4. Add some new registrations to the end of the <httpHandlers> section:

<httpHandlers>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>



5. Add a new registration to the HttpModules section, beneath any existing registrations.
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>



6. Add a SafeControl entry for the System.Web.UI namespace from Microsoft Ajax Extensions, within the <SharePoint>/<SafeControls>section:
<SafeControls>
<SafeControl Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TypeName="*" Safe="True" />
</SafeControls>



7. Finally, add the following configuration tags at the bottom of web.config, near the bottom before the end <configuration> tag.
<system.web.extensions>
<scripting>
<webServices>
<!-- Uncomment this line to enable the authentication service. Include requireSSL="true" if appropriate. -->
<!-- <authenticationService enabled="true" requireSSL = "true|false"/> -->

<!-- Uncomment these lines to enable the profile service. To allow profile properties to be retrieved and modified in ASP.NET AJAX applications, you need to add each property name to the readAccessProperties and writeAccessProperties attributes. -->
<!-- <profileService enabled="true"
readAccessProperties="propertyname1,propertyname2"
writeAccessProperties="propertyname1,propertyname2" /> -->
</webServices>
<!-- <scriptResourceHandler enableCompression="true" enableCaching="true" /> -->
</scripting>
</system.web.extensions>




OK now to add a Script Manager and Update Panel.

One way to handle the Script Manager is to add it to your Master Page(s), so it is always there when you need it.
The other way to handle it is to dynamically add it when you need it, always checking to see if it is already there first.

* Make sure you set the EnablePartialRendering = true on the Script Manager.


Where you have the WebParts set up that encapsulate the User Controls, is where you will be adding the update panel.

We have a cs file that looks like this for each of the WebParts...
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;

namespace MainSite.Template.Features.CustomWebParts.WebParts
{
public class Products : WebPartBase
{
string _deptID = string.Empty;

#region Properties
[WebBrowsable(true),
Personalizable(PersonalizationScope.Shared),
Category("Miscellaneous"),
DefaultValue(""),
FriendlyName("Department ID"),
Description("Department ID for a list of all products in that Department.")]
public string DepartmentID
{
get { return _deptID; }
set { _deptID = value; }
}
#endregion

protected override void CreateChildControls()
{
UserControlPath = "/MainSite/CustomWebParts/";
UserControlName = "Products.ascx";

MainSite.UI.UserControls.Products uControl;
this.Controls.Clear();
uControl = (MainSite.UI.UserControls.Products)this.Page.LoadControl(UserControlPath + UserControlName);
uControl.DepartmentID = this.DepartmentID;

UserControl = uControl;
AJAXupdatePanel = true;
base.CreateChildControls();
}
}
}


* You will notice I am setting a field called UserControl (inherited from WebPartBase), and if I want it, AJAXupdatePanel(inherited from WebPartBase) and the base.CreateChildControls() takes care of the rest.

Then on the WebPartBase.cs, it needs to look like this...
using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Diagnostics;

//
// Base class for use by webpart classes to dynamically load a user control into a web part
//
namespace MainSite.Template.Features.CustomWebParts.WebParts
{
public class WebPartBase : WebPart
{
private Control _userControl;
public Control UserControl
{
get { return _userControl; }
set { _userControl = value; }
}

private bool _AJAXupdatePanel;
public bool AJAXupdatePanel
{
get { return _AJAXupdatePanel; }
set { _AJAXupdatePanel = (null != value ? value : false); }
}

private void EnsureScriptManager()
{
if (ScriptManager.GetCurrent(this.Page) == null)
{
ScriptManager sm = new ScriptManager();
sm.ID = "ScriptManager1";
sm.EnablePartialRendering = true;
this.Controls.Add(sm);
}
}

private void EnsureUpdatePanelFixups()
{
if (this.Page.Form != null)
{
string formOnSubmitAtt = this.Page.Form.Attributes["onsubmit"];
if (formOnSubmitAtt == "return _spFormOnSubmitWrapper();")
{
this.Page.Form.Attributes["onsubmit"]= "_spFormOnSubmitWrapper();";
}
}
ScriptManager.RegisterStartupScript(this, this.GetType(), "UpdatePanelFixup", "_spOriginalFormAction = document.forms[0].action;_spSuppressFormOnSubmitWrapper=true;", true);
}

protected override void CreateChildControls()
{
if (AJAXupdatePanel)
{
Debug.WriteLine("AJAX Update Panel Requested.");
this.Controls.Clear();
EnsureScriptManager();
EnsureUpdatePanelFixups();

UpdatePanel up = new UpdatePanel();
up.ID = this.ID + "_AjaxUpdatePanel";
up.ChildrenAsTriggers = true;
up.UpdateMode = UpdatePanelUpdateMode.Conditional;
this.Controls.Add(up);
up.ContentTemplateContainer.Controls.Add(UserControl);
}
else
{
Debug.WriteLine("AJAX Update Panel not requested.");
this.Controls.Add(UserControl);
}
}
}
}




So whenever you want to add an AJAX Update Panel, just set AJAXUpdatePanel=true before you call base.CreateChildControls().

You could take this one step farther and make the AJAXUpdatePanel another misc property on the Products.cs page. That way the person adding the WebPart can decide if they want it to be AJAXified or not.

No comments:

Post a Comment