Provide a custom ‘new’ page for a Sharepoint Document Library

February 2, 2010

When creating Sharepoint Lists, it is possible to customize the default forms associated with the list and provide our own implementation of the display, edit and new forms. However, that doesn’t work in the same way when we are trying to customize a Document Library. In that case, we can still customize the edit and display forms (used to display and update the metadata associated with each document), but we can’t provide a custom new form. The reason is that there is no new form associated with document libraries, instead, users are redirected to an upload page where they can upload documents….


Do you want to read more ? I am moving to my new site ! Please follow the link to read all the content of this article: Provide a custom new page for a Sharepoint Document Library

Advertisements

Change an item content type with a SQL Query

December 8, 2008


I’m moving to a new site ! Please visit this article to by clicking the following url: Change an item content type with a SQL Query

Some months ago, I was involved in a document library migration from SP2001 to SP2007 project. One of the big problems I found was how to upload all the files to Sharepoint with the standard web services interface and set the content type of the items. You can read more about that issue and the solution implemented here

The problem with that approach is that it won´t work well when thousands of files are migrated. At the final migration process to production that took place this week, we moved around 70k files to the new server. Although the files were successfully uploaded, the update content type method didn´t work for many of them.

As we were in the middle of the migration process, I tried to find a different solution for bulk updating all the files and setting the correct content types. At first, I tried writing a console application that would update all the files via Sharepoint Object Model. The problem with this approach is that the process would take a lot of time to process all the 70k files.

As we had not enough time to run that process, I finally decided to try a don´t-do-this-at-home strategy. I wanted to update directly the Sharepoint database to set all the appropiate content types.

At the content database of the site collection where I was migrating all the documents, I opened the AllUserData table where all list items are stored. That table contains the fields tp_ContentType and tp_ContentTypeId that seemed like what I was looking for. Additionally, there is a ContentTypes table where all the Content Types for the SiteCollection are stored. I already knew the name of the content type I wanted to set so I only needed the Id. I tried to build a simple update query:

UPDATE    
       AllUserData
SET              
       tp_ContentType = '<content type name>', 
       tp_ContentTypeId =
                          (SELECT ContentTypeId
                            FROM   ContentTypes
                            WHERE  
                                       ResourceDir = '<content type name>')

WHERE     (tp_DirName = '<document library name>/<folder>') 

Some comments on the previous sql:

I only had one web and one document library for this migration process. In a different scenario, you would have to filter by tp_WebIdand tp_ListIdto change only the items of a specific location

I found the field ResourceDir in the Content Types table that had the name of the content type. You could also use directly the content type Id, converting it to varbinary which is the type of the tp_ContentTypeId field. However, there is a problem with that, as I’ll point out later.

Use tp_DirName to filter for a specific folder in the document library. You could also use it to filter by document library name but I strongly suggest you to use the tp_ListId field instead.

So far so good, everything looks pretty straightforward. The problem with this code, is that it won’t work as expected. The tp_ContentType field will be properly set but it won´t recognise the item with the new Content Type.

After testing the query, I decided to manually set the content type of an item with the standard Sharepoint interface and see what changed at the data level. What I saw was that the tp_ContentTypeId field was set to the id of the new content type but it also appended a Guid value at the end i.e: My content type id was “0x01017013” but the value at the AllUserData table was “0x01017013”. I didn’t know where did that guid came from nor had enough time to research it (if anybody knows it please leave a comment !!!) so I tried a different approach. What I did is, after I manually changed one of the items content type with the Sharepoint interface, I run the following Sql Query:

UPDATE    
     AllUserData
SET              
     tp_ContentType = '<content type name>', 
     tp_ContentTypeId =
                          (SELECT TOP 1 tp_ContentTypeId
                            FROM          AllUserData
                            WHERE      tp_ContentType = '<content type name>' and 
                            tp_DirName = '<document library name>/<folder>')
WHERE     (tp_DirName = '<document library name>/<folder>') 

Basically what this sql does is update all the items of the list folder and assigning the Content Type Id from the previously manually changed item (with the value assigned by the Sharepoint Object Model, including the appropiate guid value at the end)

After running that query, all the items of the list folder had the correct updated content type and in less than 1 second !

That’s all, as always, comments will be appreciated 🙂


Upload a file to Sharepoint 2007 using webservices with a specific content type

July 3, 2008


I’m moving to a new site ! Please visit this article to by clicking the following url: Upload a file to Sharepoint 2007 using webservices with a specific content type

These days I’ve been involved in a migration project from Sharepoint 2001 to MOSS 2007, what  I had to do was move all the documents from a document workspace in SPS2001 to a WSS3.0 document library. Since some of the custom metada had to be migrated with the documents and the SP2001 profiles had to be converted to MOSS content types I decided that the best approach would be to use MOSS 2007 web services interface, most specifically the Copy.asmx web service which has a CopyIntoItems method that enables to upload files to a document library.

Since SP2001 is an obsolete platform, I won’t talk here about how to access it and deal with the PKMCDO COM library (if there is enough interest, I might write an article about that) and I will focus on the file upload web service and the more challenging topic (imho) which is how to set a content type to the uploaded documents.

The first thing to do would be to get the files uploaded to MOSS. As I told, I will be using the Copy.asmx web service. Following is the code of the upload function:

        public void UploadFile(string destinationFolderPath,
                               byte[] fileBytes,
                               string fileName,
                               bool overwrite,
                               string sourceFileUrl,
                               string lastVersionUrl)
        {

            List<Sharepoint.FieldInformation> fields = new List<Sharepoint.FieldInformation>();
            Sharepoint.FieldInformation fieldInfo;

            fieldInfo = new Sharepoint.FieldInformation();
            fieldInfo.Id = Microsoft.SharePoint.SPBuiltInFieldId.Title;
            fieldInfo.Value = "New title";
            fieldInfo.DisplayName = "Title";
            fieldInfo.Type = YetAnotherMigrationTool.Library.SP2007.Sharepoint.FieldType.Text;
            fieldInfo.InternalName = "Title";
            fields.Add(fieldInfo);

            string[] url;
            if (string.IsNullOrEmpty(destinationFolderPath))
                url = new string[] { string.Format("{0}/{1}/{2}", _siteUrl, _name, fileName) };
            else
                url = new string[] { string.Format("{0}/{1}/{2}{3}", _siteUrl, _name, destinationFolderPath, fileName) };
            Sharepoint.CopyResult[] result;

            Sharepoint.Copy service = new Sharepoint.Copy();
            service.Url = _siteUrl + "/_vti_bin/Copy.asmx";
            service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);
            service.Timeout = 600000;

            uint documentId = service.CopyIntoItems(sourceFileUrl, url, fields.ToArray(), fileBytes, out result);
        }

There is nothing really difficult there. It necessary to call the webservice with an account that has Manage List permissions on the target document library. Also we should estimate the maximum document size we plan to upload and network speed and set the maxRequestLength and executionTimeout values of the httpRuntime section in our web.config in accord to avoit any possible Timeout Exception.

I’ve included an example of how would we set any of the document’s properties when calling the CopyIntoItems method. I’ve used the Title property to show that even read only fields can be assigned with that method. So, can all the fields be assigned ? Unfortunately not.

One of my requirements was to map SP2001 document profiles to MOSS content types, in order to do that, I created the content types at the server with their custom columns and looked for a way to pass this info as a parameter to the CopyIntoItems service call. There was not a parameter such like that, but I was confident that I would be able to set the ContentType field just as I did with the Title field and that would be all. However, that solution didn’t work. No matter if you set the ContentType or the ContentTypeId fields before calling the Copy.asmx service that the uploaded document content type will always be the default content type of the library.

After doing that I tried another approach. As I saw that the default content type of the library was assigned to the uploaded files, I tried to change the default content type of the list before uploading each file. Unfortunately, I couldn’t find any webservice that provided that functionality, I tried the UpdateList method of the Lists.asmx service with a custom xml scheme where the <DEFAULT></DEFAULT> section of the content type element was replaced by the one I wanted to be with no luck. I couldn’t manage to get the default content type of a document library changed !

Finally, I tried the last solution I had thought of. It was to use the UpdateListItems method of the Lists.asmx service to change the item ContentType field. The reason why I didn’t try this approach first instead of trying to change the default content type of the document library (which would seem the obvious thing to do) was because I didn’t expect it to work. If I hadn’t been able to set that readonly field with the CopyIntoItems method of the Copy.asmx service it would be expected for the UpdateListItems to have the same behaviour. But it doesn’t. It is possible to update any field with this method and eventually change a document content type.

So here is the method I use to call that service and change the content type:


        public void SetContentType(List<string> ids, string contentType)
        {
            ListsService.Lists service = new YetAnotherMigrationTool.Library.SP2007.ListsService.Lists();
            service.Url = _siteUrl + "/_vti_bin/Lists.asmx";
            service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);

            string strBatch = "";
            for (int i = 1; i <= ids.Count; i++)
            {
                strBatch += @"<Method ID='"+i.ToString()+@"' Cmd='Update'><Field Name='ID'>" + ids[i-1] + "</Field><Field Name='ContentType'>"+contentType+"</Field></Method>";
            }
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement elBatch = xmlDoc.CreateElement("Batch");
            elBatch.SetAttribute("OnError", "Continue");
            elBatch.SetAttribute("ListVersion", "10");
            elBatch.SetAttribute("ViewName", "");
            elBatch.InnerXml = strBatch;

            result = service.UpdateListItems(_name, elBatch);
        }

I called this method passing as parameters the collection of ids I got from the CopyIntoItems call, grouping by content type and it worked ! Now I can set the content type to my uploaded files and my first big issue with that migration project is solved. Currently I am working on a more complicated step which is how to migrate the version history from the SP2001 documents to the MOSS 2007, if I finally manage to solve it I’ll surely post a new article on that topic. That’s all for now, as always, comments are welcome !

Doctora Rosa Grau, Pediatra, Sant Quirze del Valles

SPWebConfigModification: configure applicationSettings with a Feature

March 5, 2008

Features are one of the coolest additions in WSS 3.0 if not the most. Not only they help us as software developers to encapsulate our code in redistributable packages but also simplify mainteinance tasks and deployment to server administrators. Ultimately, we’d like to be able to deploy our site definition with just a walk-through installer that would activate the required features.

Quite often I have to make some changes in the web.config files of the web applications where my solutions are to be deployed. Manifest.xml files of Sharepoint Solutions helps us with this task providing us with some easy ways to alter the web.config files, unfortunately, we are limited at registering safe controls and alter security policies which sometimes is not enough.

What I usually want to do, is to deploy a particular applicationSettings section for an assembly used in my solution, in this example, I have an assembly that uses an httpHandler to retrieve some Xml and I have configured the url of the handler in a settings file, this way, I’ll be able to change it if needed. This is the settings sections I need to include in the web.config file of the web application in order to do that:

<configuration>
  <configSections>
    <sectionGroup name=”applicationSettings” type=”System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″> <section name=”MyAssembly.Properties.Settings” type=”System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ requirePermission=”false” /></sectionGroup>
  </configSections>
  <applicationSettings>
    <MyAssembly.Properties.Settings>
      <setting name=”HttpHandlerUrl” serializeAs=”String”>
        <value>https://oricode.wordpress.com</value&gt;
      </setting>
    </MyAssembly.Properties.Settings>
  </applicationSettings>
</configuration>

There is no easy way to automate this process. The usual procedure would be to deploy the solution and then ask the administrators to change the web.config files of all the involved web applications manually but this is what I was trying to avoid. I decided to take a look at the new SPWebConfigModification objects in the SPWebApplication object and use them in a Feature Receiver that would modify the web.config file.

At first sight, it seems that this objects are specially suited for this task and that it would be an easy thing to do, but it is not. SPWebConfigModification class is usefull when adding and replacing sections in the configuration files but it’s not easy to modify existing ones by adding new child sections. Basically, what you do is to select a section by providing a xpath and provide the new xml value for the section, but what happens when we don’t want to replace the section but just append new child nodes to it ?

For example in my case, I had to consider the following scenarios:

1. The webconfig file didn’t had the applicationSettings section defined (which is something very usual)
2. The webconfig file already had the applicationSettings section defined (by another assembly) and I had to append the new section for my assembly in it

So I couldn’t just replace the whole section with the xml for my assembly because this might have an impact in any already configured assembly used in the web application that relied in the applicationSettings section.

So here is the code of my FeatureActivated event where I would handle those modifications:

Initialization:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = (SPSite)properties.Feature.Parent;
    SPWebApplication webApp = site.WebApplication;

Since there is no way to know if the applicationSettings section is already defined in the web.config file, my solution is to execute the modifications as if the section was defined and if that rises an exception, it would mean that it is not and then I could add it. I know it is not an elegant approach and I’d really appreciate it if anybody could come up with a better solution !

try
{
    webApp.WebConfigModifications.Clear();

    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"section[@name='MyAssembly.Properties.Settings']";
    configMod.Path = "configuration/configSections/sectionGroup[@name='applicationSettings']";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Add(configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"MyAssembly.Properties.Settings";
    configMod.Path = "configuration/applicationSettings";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "" +
                            "" +
                                "https://oricode.wordpress.com" +
                            "" +
                       "";
    webApp.WebConfigModifications.Add(configMod);

    webApp.Farm.Services.GetValue().WebConfigModifications.Clear();
    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

If the section was already defined, the previous code would launch a <em>SPException</em>. Unfortunately, since the exception is not better typed, there is no way to ensure that the error is caused by the applicationSettings section not being configured (we could always read the exception message that will tell us if so). So if the exception is raised, we have to add the sections as new <em>SPWebConfigModification</em> objects, the previous modifications were added to the <em>WebConfigModifications</em> collection of the <em>SPWebApplication</em> object so there’s no need to add them again.

catch (SPException ex)
{
    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"sectionGroup[@name='applicationSettings']";
    configMod.Path = "configuration/configSections";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = " " +
                      "";
    webApp.WebConfigModifications.Insert(0, configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"applicationSettings";
    configMod.Path = "configuration";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Insert(0, configMod);

    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

Note that this two last configuration sections have their Owner property set to a random Guid string. This is because the owner property is used in the feature deactivating event for removing the web.config changes, however, I can’t remove the applicationSettings sections because after my feature is activated, some other application settings might be registered in those sections by the server administrator and If then my feature was deactivated, all of them would be deactivated as well.

There are also a lot of issues I’ve faced when using SPWebConfigModification objects, like modifications that are persisted in the WebConfigModifications collections of the webApplication object before any call to the update() method is made (this is why I’ve set the Clear() methods of the collection at the start of the code) and other similar issues. Feel free to comment any strange behaviour you might find and I’ll try to help you If possible.

And that’s it, activating this feature will modify the web.config file of the web application as desired. However, I hope in future releases of Sharepoint object model we will see a better way to achieve this.

To end, I’d like to point that this solution should be tested in a farm deployment where multiple servers are involved, use with caution in that scenarios !

That’s all for now 🙂 as always, comments are welcome !


Sharepoint: publish a webservice with your solution

February 29, 2008

In this article I’ll cover how can you publish a WebService with your solution that will be accessible at every site in all the web applications where the solution is deployed. To keep thing simple, I’ve chosen a simple Helloworld webservice but with this approach we could publish any webservice or .aspx pages or a httpHandler

What we will do is to publish the service in the _vti_bin sharepoint folder of our site. To do this, we must first copy the .asmx file of the service in the Common FilesMicrosoft Sharedweb server extensions12ISAPI folder of our server.

<%@ WebService Language=”c#” Class=”MyAssembly.Services.HelloWorldService,MyAssembly.Services”%>

This was the .asmx file we want to distribute and following is the .DDF file from which we’ll generate the solution, I’d like to show that you could also publish a web.config file for the _vti_bin/MyAssembly folder, we would want to do so if, for example, were deploying an httpHandler instead of a webservice or if we need to configure some AppSettings parameters.

; Solution.DDF
FeaturesISAPIMyAssemblyweb.config ISAPIMyAssemblyweb.config
FeaturesISAPIMyAssemblyHerramientas.asmx ISAPIMyAssemblyHelloWorldService.asmx
ServicesbinDebugMyAssembly.Services.dll MyAssembly.Services.dll

Then we must define the manifest.xml file used in the solution deployment:

<RootFiles>
<RootFile Location=ISAPIMyAssemblyweb.config />
< RootFile Location=ISAPIMyAssemblyHelloWorldService.asmx />
</RootFiles>

<Assemblies><Assembly DeploymentTarget=WebApplication Location=MyAssembly.Services.dll /></Assemblies>

Remember to set the appropiate assembly permission at the CodeAccessSecurity section of the manifest.xml file if not running under Full Trust mode or not deploying the assembly in the GAC.

Finally, here is the easy code of our Helloworld service:

namespace MyAssembly.Services
{
    [WebService(Namespace = "http://MyAssembly.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class HelloWorldService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Greetings from this Service !";
        }

    }
}

Now, after deploying the solution we can access the webservice adding _vti_bin/MyAssembly/HelloWorldService.asmx at our site url (for those sites we have deployed the solution to !). However, If we have deployed the solution in the GAC, the service should be able to all the sites in the server.

That’s all for now ! As always, feel free to comment anything you might find interesting !


Deploy a XML file in your Sharepoint Solution to the Web Application Folder

February 27, 2008

This is a small trick I’ve been using these days in a Sharepoint Solution in order to add a xml file with some data which some webpart will read later on a Sharepoint site and make use of it. There are many ways to accomplish this each one with it’s benefits and drawbacks.

The first approach I thought of was to deploy the xml file in the _layouts folder and then query the data using a WebClient request from my webpart, the problem I found is that if anonymous access wasn’t enabled for the site, the request failed with a 401 Unauthorized exception because the ASP.Net credentials where the ones used by default to access the resource. One solution was to enable the impersonation at the web.config file of the application and then, the current user’s credentials would be used instead, but as I didn’t know where my solution would be deployed and didn’t have any control over the web.config file configuration, I rejected this approach.

Finally, I decided to deploy locally the xml file in the bin folder of the application and then accessing it with a simple StreamReader. The first problem was how to deploy the file ? Finally, I found the small trick I was talking about, you can deploy any file from a Sharepoint Solution in the bin folder of the web application just by doing the same as you do with a normal assembly .dll file, with the <Assembly> tag in the manifest.xml file:

<Assemblies><Assembly DeploymentTarget=WebApplication Location=MyData.xml /></Assemblies>

When the Solution is deployed, the xml file will be copied to all the bin folders of the selected web application. This raises the obvious drawback of this approach: There’s one xml file for each web application and there’s no easy way to globally manage them.
Since we will be accessing the xml file from our webpart, and assuming we haven’t set our trust level to Full (because we know it’s a bad thing to do!), we must grant our assembly the FileIOPermission  in order to be able to read the file. We can do this in the manifest.xml with the following section:

<CodeAccessSecurity><PolicyItem><PermissionSet class=NamedPermissionSet version=1 Description=Permission set for OurAssembly><IPermission class=System.Security.Permissions.FileIOPermission version=1 Unrestricted=true /> </PermissionSet><Assemblies><Assembly Name=OurAssembly /></ Assemblies></PolicyItem></CodeAccessSecurity>

Please note that I’ve set the unrestricted=true attribute just to keep it simple but you should set the Read attribute to the appropiate folder.Now, we can read the file from our webpart:

string loc = HttpContext.Current.Server.MapPath("/") + "bin\";
using (StreamReader reader = new StreamReader(loc + @"MyData.xml"))
{
    XDoc = XDocument.Load(reader, LoadOptions.None);
}

And that’s all, with this approach we can deploy all kinds of xml data and even .dll configuration files or any other localresource you might need. I hope it’s helpful and don’t forget to leave a comment if so !


Ajax webpart: Register scripts on UpdatePanel postbacks

February 25, 2008

These days I’m developing custom ajax enabled webparts for a sharepoint solution. I’ve created a base webpart class named AjaxEnabledWebpart from which all the webparts inherit. This webpart has an updatepanel control where all the child webparts are supposed to create the controls they need so finally, everycontrol is rendered inside the updatepanel and the webpart has the expected asp.net ajax behaviour of partial rendering the pages. Some of the webparts have to render content created dinamically by javascript code as you will see later in the code, in this case, content is rendered this way because we are supposed to give an accessible front end in our webpart (this is supporting non-script enabled browsers) so we are using the typical <noscript><script> tag approach. However, the problem we faced is that the content rendering script code (or any script code at all) was not executed during Ajax postbacks of the webpart. The solution we found to this problem was to register the scripts with the Sys.WebForms.PageRequestManager.getInstance().add_endRequest() function. Following is the code we used. First, for reference only, I’ll post the main code of the AjaxEnabledWebpart which somebody might find useful

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

    EnsurePanelFix();

    _updatePanel = new UpdatePanel();

    _scriptHandler = ScriptManager.GetCurrent(this.Page);
    if (_scriptHandler == null)
    {
        //no script manager registered
        _scriptHandler = new ScriptManager();
        _scriptHandler.ID = "scriptManager";
        this.Controls.Add(_scriptHandler);
    }

    _updatePanel.ID = "updatePanel_";
    _updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
    _updatePanel.ChildrenAsTriggers = true;

    CreatePanelControls(_updatePanel.ContentTemplateContainer.Controls);

    this.Controls.Add(_updatePanel);
}

protected virtual void CreatePanelControls(ControlCollection PanelControls)
{
}

The only relevant code from the AjaxEnabledWebpart is the CreateChildControls method, which creates the UpdatePanel control and calls the virtual method CreatePanelControls passing as a parameter the control collection where child pages are supposed to add the controls. Also, you can find the EnsurePanelFix implementation on my previous post at this linkAfter that, here is the code of a custom HtmlTable used in some webparts which contents are rendered using javascript code (if scripting is enabled in the client browser). The posted code is a function which returns the html code to be inserted in a cell’s InnerHtml property of a table:

private string _getSelectAllHtml(HtmlTableCell cell)
{
    string ret = "";

    ret += "" +
                "select all" +
             "" +
             "" +
                "// " +
            "" +
           "";

    string fn = "" +
                    "//<!&#91;CDATA&#91; n" +
                        "function _loadSelectAllHeader() { " +
                            "cab = '<a href="#" title="CellTitle">" +
                            "<img src="_layouts/img/selectall.gif" alt="select all">';" +
                            "var capa = document.getElementById("" + cell.Attributes&#91;"Id"&#93; + "");" +
                            "capa.innerHTML = cab;n" +
                        " }" +
                    "//&#93;&#93;> " +
                "";

    Page.ClientScript.RegisterClientScriptBlock(typeof(ResultsTableMod47),
                                "_loadSelectAllHeader",
                                fn);

    Page.ClientScript.RegisterStartupScript(typeof(ResultsTableMod47),
                                "initRequestScriptHandler_loadSelectAllHeader",
                                "Sys.WebForms.PageRequestManager.getInstance().add_endRequest(_loadSelectAllHeader);");

    return ret;
}

The relevant lines of the code are the calls to RegisterClientScriptBlock and RegisterStartupScript methods. The first one register our content-rendering script while the second one adds this function to the end_request handler of the PageRequestManager instance. This will ensure that the script is called after each Ajax postback of the pages.Further scripts can be registered in the end_request handler by calling the add_endRequest method for each script we want to be executed after a postback. However, we have to considered that this script will be executed for all the postbacks and not only for the ones generated by this webpart’s updatepanel control, so if more than one updatepanel is present on the page, we might have to include some checking at the startup of our code to ensure that our script must be executed. In this case this was not necessary since the script could be executed again without any problem.

That’s all for now ! I hope it’s useful…. coments are welcome 🙂