Prexens team dev' corner

SPDisposeCheck (SharePoint Dispose Checker Tool) helps you to follow "Best Practices: Using Disposable Windows SharePoint Services Objects"

Quick description of the tool (abstract from Microsoft SharePoint Team Blog)
 
 
SPDisposeCheck v1.3.1 has been released on MSDN Code Gallery, http://code.msdn.microsoft.com/SPDisposeCheck.  This tool will help improve the quality of your SharePoint assemblies.  It will inspect your SharePoint assemblies and check that you are correctly disposing of certain SharePoint objects (IDisposable objects which includes SPSite and SPWeb).  The tool is based upon the guidance published in this MSDN article, Best Practices: Using Disposable Windows SharePoint Services Objects
 
This tool is not supported by Microsoft and is recommended to be used on Developer workstations and not on production SharePoint Server installations.  For additional information, please head over to Paul Andrew's blog post.
 
How to use it
  1. Install SPDisposeCheck.msi on your development server.
  2. Open a command window.
  3. Type following command:
    "c:\Program Files\Microsoft\SharePoint Dispose Check\SPDisposeCheck.exe" <path to assemblies> -debug

Best practices and common errors when adding a ScriptManager programmatically from a WebPart

Goal of this post
 
This post aims at providing the most flexible and convenient way to make sure that a valid ScriptManager is available on page when you develop WebParts dealing with AJAX for ASP.net or Silverlight controls.
 
Problematic overview
 
ScriptManager is the key object making Silverlight and AJAX for ASP.net controls run on your ASP.net page. This non-visual control basically aims at loading JS scripts relevant for those technologies. Referring to MS documentation, it must appear as the first control in page controls tree in order to be available for any control requiring AJAX support. A ScriptManager can be embedded either from markup code of the hosting page, or programmatically during page lifecycle.
 
So let’s imagine you are developing a WebPart embedding a Silverlight application. You want to redistribute this WebPart but you don’t want users to modify anything in their SharePoint portal to make it run. Basically, in your code, you will try to see if a ScriptManager is present on the page and if no you will add it on the fly.
 
Then you have to face several issues:
  1. Where can I place my ScriptManager in the controls tree to make it available to relevant controls?
  2. Which event do I need to choose to attach my code?
  3. Will it be compliant with postback events, databinding cycles and WebParts connection management?
Solution
 
First, you need to add a new ScriptManager as the first control in the page form. Before adding anything, you have to ensure that no ScriptManager is already present on the page.
if (Page != null && ScriptManager.GetCurrent(Page) == null)
{
    Page.Form.Controls.AddAt(0, new ScriptManager());
}
Then you need to determine where to place this code to be compliant with requirements evoked at question #3. The most reliable answer is to override the OnInit event of your WebPart and paste your code there
protected override void OnInit(EventArgs e)
{
    if (Page != null && ScriptManager.GetCurrent(Page) == null)
    {
        Page.Form.Controls.AddAt(0, new ScriptManager());
    }

    base.OnInit(e);
}
An alternative approach could be to attach a handler to the page LoadComplete event, but it gives no gain to the previous solution.
 
Common errors
 
I’ve listed below some common mistakes and the corresponding errors when trying to answer to questions above.
Do not add control directly to Page’s controls collection; add it to its Form property.
Page.Controls.AddAt(0, new ScriptManager());
Page.Form.Controls.AddAt(0, new ScriptManager());
Striked code above will produce the following error
 
Control 'ctl01' of type 'ScriptManager' must be placed inside a form tag with runat=server
 
Do not try to override the WebPart OnLoad event to place your code.
If so, you will receive the error below
 
The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.
 
Do not try to add any handler to page events.
Web parts lifecycle is too restrictive to use this approach.
    • If you try to attach a handler to the Page Init event, your code will never be executed because Web parts are added after the page InitComplete event.

    • If you try to attach a handler to the Page Load or PreLoad events, your code will never be executed when your Web part is being added to the page and you will receive the error below (next attempts will work). The fact is that when a Web part is added to a page, any page event prior to LoadComplete is ignored.
The control with ID 'XXX' requires a ScriptManager on the page. The ScriptManager must appear before any controls that need it.
    • If you try to attach a handler to the Page PreRender event, you will receive the error below
Script controls may not be registered before PreRender.

Ultimate solution to get rid of "The Web site wants to run the following add-on: 'Name ActiveX Control'"

Download setup files (21.1 KB)
Download sources (926 KB)
 
Problematic overview
 
When you are developing internet or extranet website with SharePoint, a common issue you have to resolve is to fix the following message displayed to end-users running IE7 and later
 
The Web site wants to run the following add-on: 'Name ActiveX Control'
 
This message is caused by presence features of SharePoint requiring this ActiveX control to be installed on clients computer. Many solutions can be found on the Web. Even Microsoft gives some workarounds to get rid of this behavior. But to me, none of them are really reliable.
 
Let's study a bit solutions from Microsoft. First and second methods require doing something on clients computer... Hmmm, seems to be a bit tricky in case of an internet or extranet Web site!
 
Solution #3 is to duplicate and rename the JS file which contains the presence script, comment out some code and register this new version by specifying its name through the name attribute of a ScriptLink object. Great, it does the trick! But what if I want to be a good guy and do not load core.js for my branding website? Let's remind the solution for this tweak: I need to empty script link of any name attribute... a dog eating his tail. And what if I want to have presence features enabled when my users are Windows authenticated?
 
A true Best-of-Breed solution: Control Adapters
 
Control Adapter is a very handy solution when you want to update the renderings of WebControls you are not responsible for. In our case, the trick is to create a control adapter for Microsoft.SharePoint.WebControls.ScriptLink and change the rendering behavior when authentication method is not Windows based. If the user is anonymous or form-based authenticated, we load a light version of the presence JS script and we suppress core.js.
 
Let's talk about code
 
First, we create a class library project in Visual Studio, sign the assembly and create a class ScriptLinkAdapter inheriting from System.Web.UI.Adapters.ControlAdapter.
 
Then, we override the Render method and we paste the code below.
 
   1:  // Retrieve authentication type
   2:  string authenticationType = HttpContext.Current.User.Identity.AuthenticationType;
   3:   
   4:  // Replace output with a minimal output if authentication is anonymous or forms-based
   5:  if (!HttpContext.Current.Request.IsAuthenticated ||
   6:      String.IsNullOrEmpty(authenticationType) ||
   7:      authenticationType.Equals("Forms", StringComparison.InvariantCultureIgnoreCase))
   8:  {
   9:   
  10:      // Load the ouputted HTML in memory
  11:      using (MemoryStream memoryStream = new MemoryStream())
  12:      using (StreamWriter streamWriter = new StreamWriter(memoryStream))
  13:      {
  14:          using (HtmlTextWriter htmlTextWriter = new HtmlTextWriter(streamWriter))
  15:          {
  16:              Control.GetType().InvokeMember(
  17:                  "RenderControl",
  18:                  BindingFlags.InvokeMethod 
  19:                  | BindingFlags.Instance 
  20:                  | BindingFlags.NonPublic,
  21:                  null,
  22:                  Control,
  23:                  new object[] { htmlTextWriter, null });
  24:          }
  25:   
  26:          // Flush the writer and rewind the stream
  27:          streamWriter.Flush();
  28:          memoryStream.Position = 0;
  29:   
  30:          // Read the stream as a string
  31:          using (StreamReader streamReader = new StreamReader(memoryStream))
  32:          {
  33:              string output = streamReader.ReadToEnd();
  34:   
  35:              // Remove core.js
  36:              string corePattern = @"\<script.+src\=.+core\.js.+\<\/script\>";
  37:   
  38:              output = Regex.Replace(output, corePattern, "", RegexOptions.IgnoreCase);
  39:   
  40:              // Replace init.js by a version excluding presence script
  41:              string initPattern = @"(\<script.+src\=\"")(init\.js)(\"".+\<\/script\>)";
  42:   
  43:              if (Regex.IsMatch(output, initPattern))
  44:              {
  45:                  output = Regex.Replace(output,
  46:                      initPattern,
  47:                      String.Format("$1{0}$3", Page.ClientScript.GetWebResourceUrl(
  48:                              typeof(ScriptLinkAdapter),
  49:                              "ScriptLinkAdapter.ClientResources.SPInitNoPresence.js")),
  50:                      RegexOptions.IgnoreCase);
  51:              }
  52:   
  53:              // Flush new content to response
  54:              writer.Write(output);
  55:          }
  56:      }
  57:   
  58:      // Registrations are done, so exit.
  59:      return;
  60:  }
  61:   
  62:  // Standard control rendering
  63:  Control.GetType().InvokeMember(
  64:      "RenderControl",
  65:      BindingFlags.InvokeMethod 
  66:      | BindingFlags.Instance 
  67:      | BindingFlags.NonPublic,
  68:      null,
  69:      Control,
  70:      new object[] { writer, null });
 
First, we retrieve the authentication type to apply transformation only for anonymous and forms authenticated users (line 1 to 8)
 
A key thing to know about control adapters is how to render the adapted control in standard way if no transformation has to be applied. A common mistake is to make a call to the RenderControl of the adapted control. If you do so you will enter an infinite loop: all controls are designed to look for an adapter before rendering themselves, if an adapter is found then the adapter is used and if the adapter call then render method again, the control will look for an adapter which calls the render... the dog and his tail again!

The solution is to use reflection to call a protected version of RenderControl that take a ControlAdapter as an input parameter. If we pass null, no adapter is assumed and the normal rendering cycle is called (line 14 to 24).
 
We are now able to get the original HTML content produced when rendering a ScriptLink control normally. We load this content into a MemoryStream, make some changes on it to suppress core.js and replace the old version of init.js and we flush this new content to the HtmlTextWriter attached to the Response object (line 33 to 54).
 
How to deploy
  1. Register assembly Prexens.SharePoint.WebControls.Adapters.ScriptLink.dll to the GAC of your SharePoint server
  2. Copy file Prexens.SharePoint.WebControls.Adapters.ScriptLink.browser to the folder App_Browsers of the application folder of your Web application (typically C:\Inetpub\wwwroot\wss\VirtualDirectories\<Your App Pool>)
  3. IMPORTANT: open the file App_Browsers\compat.browser in Notepad and save it (no need to change anything). This will force reloading the browser files. If you have multiple browser files already present you might need to repeat for each one.
  4. Restart IIS services (iisreset.exe in a command prompt)

Categories
Links
  Prexens Web Site
  Charting for SharePoint
  Charting for SharePoint (Codeplex)
  Charting for SharePoint Starter kit

Microsoft Certified Partner