Friday, February 27, 2009

Multiple Field Custom Validator

I needed to extend the Custom Validator to allow it to have multiple ControlToValidate fields, so that if any of the fields are changed the validator would trigger.

Example:

Credit Card Expiration Date - I had two dropdownlists for the month and year. I wanted a custom validator that instantly triggered a warning if the two fields did not combine into a date that was current or future.

I was able to create the JavaScript on the client side to check for that, but I would have to have 2 custom validators, one for each dropdownlist. So if I selected a new year but the combined was not current or future then the warning would show. But if I then changed the month and the combined was now current or future, it would not know to go back to the previous custom validator and validate it, since it had its own validator.

I know I could have hard coded the JavaScript to do it, but I wanted something more substantial and reusable.

So I decided to create a multi-field custom validator, just extending the .NET Custom Validator.


Here are the steps I took to create this.

1. Start a new class project.

2. Create the new class inheriting from System.Web.UI.WebControls.CustomValidator. Also includes settings for the control.

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing;

namespace My.CustomControls
{
   [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
   [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
   [ToolboxData(@"<{0}:MultipleFieldsValidator runat=server></{0}:MultipleFieldsValidator>")]
   public class MultipleFieldsValidator : System.Web.UI.WebControls.CustomValidator
   {
   }
}

3. Add a Private Property to store the extra controls to validate.
        #region Private Properties

   private string _extraControlsToValidate;

#endregion

4. Add a Public Property to Get/Set the extra controls to validate.

#region Public Properties

   /// <summary>
   /// Comma separated list of control IDs that you want to check
   /// </summary>
   [Browsable(true)]
   [Category("Behavior")]
   [Themeable(false)]
   [DefaultValue("")]
   [Description("Comma separated list of extra control IDs that you want to check")]
   public string ExtraControlsToValidate
   {
     get
     {
       return _extraControlsToValidate;
     }
     set
     {
       _extraControlsToValidate = value;
     }
   }

#endregion

5. Create some helper methods to parse out that field.

#region Helper Methods

private string[] GetControlsToValidateIDs()
{
   try
   {
     string[] controlToValidateIDs = null;
     if (this.ExtraControlsToValidate != null && this.ExtraControlsToValidate.Length > 0)
     {
       string controlsToValidate = this.ExtraControlsToValidate.Replace(" ", "");
       if (controlsToValidate.Length > 0)
       {
         try
         {
           controlToValidateIDs = controlsToValidate.Split(',');
         }
         catch (ArgumentOutOfRangeException ex)
         {
           throw new FormatException(string.Format("The ExtraControlsToValidate property of {0} is not well-formatted.", this.ID), ex);
         }
       }
     }
     return controlToValidateIDs;
   }
   catch (Exception e)
   {
     throw (e);
   }
}

// Only needed if doing the RegisterExpandoAttribute in AddAttributesToRender
private string GenerateClientSideControlsToValidate()
{
   try
   {
     string[] controlToValidateIDs = this.GetControlsToValidateIDs();
     string controlToValidateIDTrimmed;
     string controlRenderIDs = string.Empty;
     foreach (string controlToValidateID in controlToValidateIDs)
     {
       controlToValidateIDTrimmed = controlToValidateID.Trim();
       if (controlToValidateIDTrimmed == string.Empty)
       {
         throw new FormatException(string.Format("The ExtraControlsToValidate property of {0} is not well-formatted.", this.ID));
       }
       controlRenderIDs += "," + base.GetControlRenderID(controlToValidateIDTrimmed);
     }
     controlRenderIDs = controlRenderIDs.Remove(0, 1); // Removing the first ","
     return controlRenderIDs;
   }
   catch (Exception e)
   {
     throw (e);
   }
}

#endregion

6. Override a method (OnPreRender) to attach the validator to the extra controls.

#region Overiden Methods

protected override void OnPreRender(EventArgs e)
{
   try
   {
     base.OnPreRender(e);
     if (base.RenderUplevel)
     {
       if (!Page.ClientScript.IsClientScriptBlockRegistered("MultipleFieldsValidator"))
       {
         if (null != this.ExtraControlsToValidate && this.ExtraControlsToValidate.Length > 0)
         {
           System.Text.StringBuilder sb = new System.Text.StringBuilder();
           sb.Append("<script language='javascript' type='text/javascript'> \n");
           sb.Append("<!-- \n");
           sb.Append("var ctrl=''; \n");
           sb.Append("var vld=''; \n");
           string[] extraControls = GetControlsToValidateIDs();
           foreach (string controlToValidateID in extraControls)
           {
             string controlToValidateIDTrimmed = controlToValidateID.Trim();
             if (controlToValidateIDTrimmed != string.Empty)
             {
               sb.Append("ctrl = document.all ? document.all['" + base.GetControlRenderID(controlToValidateIDTrimmed) + "'] : document.getElementById('" + base.GetControlRenderID(controlToValidateIDTrimmed) + "'); \n");
               sb.Append("vld = document.all ? document.all['" + this.ClientID + "'] : document.getElementById('" + this.ClientID + "'); \n");
               sb.Append("if (typeof(ctrl) != 'undefined') \n");
               sb.Append("{ \n");
               sb.Append(" ValidatorHookupControl(ctrl, vld); \n");
               sb.Append("} \n");
             }
           }
           sb.Append("--> \n");
           sb.Append("</script> \n");
           this.Page.ClientScript.RegisterStartupScript(this.GetType(), "MultipleFieldsValidator", sb.ToString());
         }
       }
     }
   }
   catch (Exception ex1)
   {
     throw (ex1);
   }
}

#endregion

7. (Optional) Override a method (AddAtrributesToRender) to put a Custom (Expando) Attribute on the validator control if the developer wants access to that in the client side function they add to the custom validator.

#region Overiden Methods

protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
{
   try
   {
     base.AddAttributesToRender(writer);
     if (this.RenderUplevel)
     {
       string clientID = this.ClientID;
       if (this.ExtraControlsToValidate != null && this.ExtraControlsToValidate.Length > 0)
       {
         Page.ClientScript.RegisterExpandoAttribute(clientID, "extracontrolstovalidate", this.GenerateClientSideControlsToValidate());
       }
     }
   }
   catch (Exception ex1)
   {
     throw (ex1);
   }
}

#endregion

8. (Optional) Add a png file to the project for use in the toolbox. Right click on it, properties, Build Action, select “Embedded Resource”. Then add this setting to the class.

[ToolboxBitmap(typeof(MultipleFieldsValidator), "MultiFieldValidator.png")]
public class MultipleFieldsValidator : System.Web.UI.WebControls.CustomValidator

9. (Optional, but required if putting in the GAC or bin folder for SharePoint) Strongly type the project. Right click on solution name, properties. Signing, Choose a strong name key file, select new, Enter Key file name, uncheck “Protect my key file with a password unless you want to have that.

10. Build the project.

11. Copy DLL to your main project’s bin folder or to the GAC if you have problems with permissions and don’t have time to modify the permission policy.

12. Add a reference to the DLL in your main project.

13. (Optional) While opened to a web page (aspx, ascx, etc), right click on a header in the Toolbox and click on Choose Items, “.NET Framework Components”, Browse. Find the DLL and select it, then OK. This will add the new MultiFieldCustomValidator to your Toolbox.

14. If added to Toolbox, drag and drop onto webpage where you want the control. It will add a Register directive at the top of the page. If you don’t add to the Toolbox, you need to first add the Register directive, then:
<%@ Register Assembly="My.CustomControls" Namespace="My.CustomControls" TagPrefix="cc3" %>

(Optional) If the full assembly is needed like for SharePoint then:
<%@ Register Assembly="My.CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxxx" Namespace="My.CustomControls" TagPrefix="cc3" %>

15. (Optional) If using SharePoint, then add a “SafeControl” to the web.config so SharePoint trusts this DLL.
<SafeControls>
   <SafeControl Assembly="My.CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4abcb93e1cb47678" Namespace="My.CustomControls" TypeName="*" Safe="True" />
</SafeControls>

16. Add the properties to the control just like you would with the CustomValidator, but add one extra property, ExtraControlsToValidate, which is a comma delimited list of all the ID’s of the extra controls you want attached to this validator.

Now you have your Multi-Field Custom Validator ready to use in any of your projects.

Friday, February 6, 2009

SSL for only some pages in SharePoint

I had a client that wanted SSL for only a few of the pages on their website, Login Page, all Checkout pages, and Account Management pages. But they didn't want SSL on all the other pages because they want the pages to load as fast as possible.

After much research I found one blog that really helped me get this accomplished.
http://sanmuki.blogspot.com/2008/07/enable-ssl-for-selected-pages-in-moss.html

I'll list below the steps I needed to take to make this happen.

Usually you have to find a way to handle the session state when switching between HTTP and HTTPS, but MOSS already has that ability.

1. First create a class that will be used to check if URLs need to be SSL'ed or not and redirect URLs as needed.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Security;
using System.Diagnostics;
using System.Web.UI;

namespace Project.Business
{
public class ForceSSL : System.Web.IHttpModule
{
#region IHttpModule Members

public void Dispose()
{
//throw new NotImplementedException();
}

public void Init(System.Web.HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}

#endregion

void context_BeginRequest(object sender, EventArgs e)
{
RedirectSSL(System.Web.HttpContext.Current.Request.Url.PathAndQuery);
}

/// <summary>
/// Method to switch between SSL and non-SSL.
/// </summary>
/// <param name="pageURL">URL that needs to be looked up for SSL requirement.</param>
/// <returns></returns>
public void RedirectSSL(string pageURL)
{
//---------------------------------------------------
// Init VARs
//---------------------------------------------------
bool found = false;
//--- Get Pages from web.config that need to be SSL'ed
string sslPages = System.Configuration.ConfigurationSettings.AppSettings["SSLPages"];

if (!(null == sslPages))
{
//---------------------------------------------------
// Parse out the list of Pages that need SSL
//---------------------------------------------------
char[] separator;
separator = new char[] { ',' };
string[] pages;
pages = sslPages.Split(separator);
for (int i = 0; i < pages.Length; i++)
{
//---------------------------------------------------
// Check if URL passed to method is in the list
//---------------------------------------------------
if (pageURL.ToLower().Contains(pages[i].ToLower()))
{
found = true;
break;
}
}
}

if (found) //-- If page needs to be SSL
{
//---------------------------------------------------
// If page needs to be SSL but is not already set as SSL
//---------------------------------------------------
if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "http")
{
//---------------------------------------------------
// Init VARs. Get the SSLPort from web.config
//---------------------------------------------------
string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
string sPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
string sslPort =System.Configuration.ConfigurationSettings.AppSettings["SSLPort"];
//-- getting rid of the double slashes to make this process easier.
sURL = sURL.Replace("http://", "http");
//-- if SSL is on port 443, then the :443 is not needed, but anything else it is need (i.e. :444)
if ((null == sslPort) || sslPort == "443")
{
sslPort = "";
}
else
{
sslPort = ":" + sslPort;
}
//-- if coming from a http port other than 80 then replace with new port else add the new port
if (sURL.IndexOf(":") > 0)
{
sURL = sURL.Replace(":" + sPort, sslPort);
}
else
{
sURL = sURL.Substring(0, sURL.IndexOf("/")) + sslPort + sURL.Substring(sURL.IndexOf("/"));
}
//-- add back the double slashes and include the "s" to http
sURL = sURL.Replace("http", "https://");
//-- debugging to know it was redirected
Debug.WriteLine("Redirected SSL URL: " + sURL);
//-- redirect to the new URL
System.Web.HttpContext.Current.Response.Redirect(sURL);
}
}
else //-- If pages needs not be SSL
{
//---------------------------------------------------
// If page needs not be SSL but is SSL currently
//---------------------------------------------------
if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "https")
{
//---------------------------------------------------
// Init VARs. Get the non-SSL port from web.config
//---------------------------------------------------
string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
string sslPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
string sPort = System.Configuration.ConfigurationSettings.AppSettings["NonSSLPort"];
//-- getting rid of the double slashes to make this process easier.
sURL = sURL.Replace("https://", "https");
//-- if non-SSL is on port 80, then the :80 is not needed, but anything else it is need (i.e. :81)
if ((null == sPort) || sPort == "80")
{
sPort = "";
}
else
{
sPort = ":" + sPort;
}
//-- if coming from a http port other than 443 then replace with new port else add the new port
if (sURL.IndexOf(":") > 0)
{
sURL = sURL.Replace(":" + sslPort, sPort);
}
else
{
sURL = sURL.Substring(0, sURL.IndexOf("/")) + sPort + sURL.Substring(sURL.IndexOf("/"));
}
//-- add back the double slashes and exclude the "s" from https
sURL = sURL.Replace("https", "http://");
//-- debugging to know it was redirected
Debug.WriteLine("Redirected non-SSL URL: " + sURL);
//-- redirect to the new URL
System.Web.HttpContext.Current.Response.Redirect(sURL);
}
}
}
}
}


2. Build this code, sign the assembly and put it in the GAC.

3. Add lines to web.config...

<httpModules>
<add name="ForceSSL" type="Cook.Business.ForceSSL, Cook.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx"/>
</httpModules>



<appSettings>
<!-- Switching between HTTP/HTTPS -->
<add key="SSLPort" value="443"/>
<add key="NonSSLPort" value="2222"/>
<add key="SSLPages" value="/Pages/UserLogin.aspx,/Checkout/CheckoutPayment.aspx,/Checkout/CheckoutShipping.aspx,/Checkout/CheckoutReview.aspx,/Checkout/CheckoutConfirmation.aspx"/>
</appSettings>



4. We need an SSL certificate in order to use SSL.
  a. If you already have an SSL certificate on the server move on to #5
  b. To create your own certificate you can use SelfSSL from Microsoft
    b1. Download the IIS 6.0 Resource Kit Tools
    b2. Install the tools on the server
    b3. Run SelfSSL. Make sure you put the length of time out farther than a week (my mistake) so it will last as long as your project will last.

5. Now we need to set up the website in IIS for SSL.
  a. Go to Start -> Administrative Tools -> Internet Information Services (IIS) Manager
  b. Expand the IIS server
  c. Expand the Web Sites folder
  d. Go to properties of the website
  e. Inside properties, navigate to Directory Settings -> Secure Communications -> Server Certificate
  f. Assign the existing certificate. This is from #4

6. Inside Directory Security, go to secure communications -> edit, and make sure Require secure channel (SSL) is NOT checked.

7. On the Web Site tab (same level as Directory Security) add an SSL port. The default is 443. If you use 80 and 443 for TCP port and SSL port respectively then your URL will not need the ports mentioned in the URL, but the code above handles that if you do.

8. The post that I learned this from needed one more step that I did not need. ***OK I was wrong. I did not need this on my development server, but I did on the live server. The difference seems to be FQN (Fully Qualified Name). Without this step, I'd get sqlsessionstateresolver errors much of the time on the live server.
  a. Go to the Central Administration website
  b. Go to Operations -> Alternate Access Mappings
  c. Add the new URL via internal URL's link.
https://servername/

9. Perform an IISReset
  a. Go to a command prompt and type iisreset

You should now be able to go on to your website and see the pages in your web.config list are SSL while everything else is not SSL.

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.