Custom List Forms with Code-behind 

There are a lot of articles on the web about manually modifying the list forms and the ListFormWebPart in particular, but there isn't that much information about modifying these forms programmatically, and the problems that come with that.
 
To start with, there's a great article on SharePoint List Forms by Hristo Pavlov that I highly recommend.
 
Customizing the ListFormWebPart is a chapter by itself, but what we needed was to customize the EditForm.aspx. Actually, we needed to inject a control that would prevent the form to come up while the asynchronous "ItemAdded" event, triggered by a file upload to the library, was still busy retrieving metadata (also an adventure in itself, as I consider that clearly a conceptual bug in SharePoint), which would result in an exception and a failure of the upload and/or the metadata extraction.
 
Now, there are actually two levels of forms one could utilize: each list/library has a set of these forms (New, Display, Edit) and the content types that are associated with the list/library also have these forms.
 
We chose to replace the EditForm.aspx of the document-type content types with a custom aspx form with code-behind. Why code-behind? Well, there are actually a couple of things one needs to adjust on the fly to make this work. One example would be that a custom application page should reside in the "_layouts/" space and the default.master masterpage that is used by the list forms is not directly accessible from there. But the most important reason was that the ListFormWebPart requires the "ListName" (it's actually the Guid ID of the list) and the "ListItemId" (which is what it sounds like :-) to be set properly when the OnInit event fires.
 
ListName&ListItemId
 
So, where's the problem? The ListFormWebPart is an incredibly flexible piece of programming art that can render itself on the fly automatically, depending on what list, content type and columns are to be displayed, BUT it does not retrieve the ListName and ListItemId from the Page.REQUEST automatically. Alright, we can do that with the code-behind, but that's NOT the problem ;-) The problem is that the control isn't available during the OnPreInit event to set these properties, and before the OnInit event handler gets fired SharePoint already throws an exception stating that no item could be found for the list and itemId specified ...Duhh!
 
Well, the solution is actually (as most things in life) trivial: the OnInit event fires on the controls before it fires for the page. Therefore it is important to add a code-behind event handler not only at the page level but also for the Web Part itself:
 
WebPart OnInit event handler registration
 
And the rather simple code-behind for this looks like this:
 
        protected void Lfwp_OnInit(object sender, EventArgs e)
        {
            string listId = Page.Request.QueryString["List"];
            string itemId = Page.Request.QueryString["ID"];

            ListFormWebPart lfwp = (ListFormWebPart)sender;
            lfwp.ListName = listId;
            lfwp.ListItemId = Convert.ToInt32(itemId);
        }

 
The beauty of this solution is that the same custom EditForm.aspx can then be used not only for all Picture Libraries, but also for Document Libraries.
 
Posted on 12-Oct-09 by Jennifer Neumann
8 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
Tags: Code, Sharepoint, WCM, Metadata, Digital Asset Management
 

Comments

Thursday, 7 Jan 2010 02:43 by Gareth Suarez
but where do you put the codebehind? I have a custom aspx page that is basically identical to the editform, except in the page declaration I inherit from a dll with my codebehind in it. setting the oninit parameter on the aspx page doesn't cause anything to get called on the codebehind page when I debug it.

Thursday, 7 Jan 2010 12:34 by Jen
Gareth, here's the line that links our editform to the codebehind: <%@ Page language="C#" MasterPageFile="~/_layouts/application.master" Inherits="FC.MetadataExtractor.PicEditForm, FC.MetadataExtractor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6ca8fb5d4afa6f3d" %> That's all you need to change on the aspx page. However, you also need to "tell" the list or content type that it should use your editform instead of the default one.

Thursday, 7 Jan 2010 10:11 by Gareth Suarez
right, I understand that part. My page declaration looks like: <%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="WTI.EmployerPortal.UserEditForm,ePortal.WebInterfaces,Version=1.0.0.0,Culture=neutral,PublicKeyToken=9f4da00116c38ec5" %> I have <WebPartPages:ListFormWebPart ID="ListFormWebPart1" runat="server" __MarkupType="xmlmarkup" WebPart="true" OnInit="wp_OnInit" my code behind starts as follows: namespace WTI.EmployerPortal { public class UserEditForm : Microsoft.SharePoint.WebPartPages.WebPartPage { protected global::Microsoft.SharePoint.WebPartPages.WebPartZone Main; protected void wp_OnInit(object sender, EventArgs e) { string listId = Page.Request.QueryString["List"]; string itemId = Page.Request.QueryString["ID"]; ListFormWebPart lfwp = (ListFormWebPart)sender; lfwp.ListName = listId; lfwp.ListItemId = Convert.ToInt32(itemId); } the problem is my wp_oninit code in my codebehind is never getting called. do I have it in the wrong place?

Thursday, 7 Jan 2010 10:31 by Jen
Gareth, I think you're missing the "partial" in " public partial class UserEditForm : Microsoft.SharePoint.WebPartPages.WebPartPage".

Thursday, 7 Jan 2010 11:01 by Gareth Suarez
Added the partial to the class, and still the oninit is not calling my function in my code behind. sometimes I think sharepoint is out to get me.

Thursday, 7 Jan 2010 11:10 by Jen
I know how that feels ;-) ... So how did you exactly hook up your UserEditForm to the list or content type? Maybe that's not working. Also, just to rule out that it's not a problem with the wp codebehind, you could put in a "protected override void OnPreInit(EventArgs e) { base.OnPreInit(e); }" and put a breakpoint on it.

Thursday, 7 Jan 2010 11:25 by Gareth Suarez
we have a feature that we deploy on each site that acts as an upgrade script. in the feature receiver I added the new aspx page to the editformurl property. when I edit a list item, it is definitely using my new page, and breakpoints set on the page load in the code behind are being hit. its just the event from the webpart that doesn't seem to be wired up properly.

Thursday, 7 Jan 2010 11:31 by Jen
Gareth, here's my ListFormWebPart declaration... <WebPartPages:ListFormWebPart ID="ListFormWebPart1" OnInit="Lfwp_OnInit" webpart="true" runat="server" __WebPartId="{51C3F707-B5E4-408B-9ED8-9AA8C394ADB6}"> <WebPart xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/WebPart/v2"> <FrameType>None</FrameType> <FormType xmlns="http://schemas.microsoft.com/WebPart/v2/ListForm">6</FormType> <Toolbar xmlns="http://schemas.microsoft.com/WebPart/v2/ListForm"></Toolbar> <PartOrder>2</PartOrder> <ID>g_51c3f707_b5e4_408b_9ed8_9aa8c394adb6</ID> </WebPart> </WebPartPages:ListFormWebPart> HTH

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation