Redirecting from NewForm.aspx to DispForm.aspx after creating a new item

The requirement

Recently a simple requirement came up: “Redirect the user to the view-form of an item after he creates a new item”. Sounds simple, right? …

The default behavior of SharePoint is to redirect the user to the url defined by the Source-parameter. If this parameter is not given the default view (mostly AllItems.aspx) will be used.

The result

The implementation of the requirement can be seen in this short screencast. After the user creates a new item he will be redirected to review the item-properties.

The way it should be

Not amazed by the pictures you just saw? One would think its easy to achieve this behavior. Simply add an EventReceiver to the desired list and listen to the ItemAdded-event and redirect the user…
In theory this is should work. The problem is that there is no HttpContext.Current available inside the ItemAdded-method.

The HttpContext

The only way to get the current HttpContext is within the constructor of your custom EventReceiver during a asynchronous event synchronous event (ItemAdding, ItemUpdating) as shown in the following pseudo code.

public class CustomEventReceiver : SPItemEventReceiver
{
    private HttpContext _currentContext = null;
 
    public CustomEventReceiver () : base ()
    {
        if (null != HttpContext.Current)
        {
            _currentContext = HttpContext.Current
        }
    }
 
    public override void ItemAdding (SPItemEventProperties properties)
    {
       // Here one can use _currentContext and redirect ...
    }
}

But now another problem arises. If you redirect the current thread is aborted and the item is never added to the list/library

// Will abort the thread
_currentContext.Redirect("http://somewhere/to/go");
 
// Will NOT abort the thread but the redirection is ignored
_currentContext.Redirect("http://somewhere/to/go", false);
 
// What a mess!

The magic

The only possible solution is to “manually” add the item so the redirection works as shown in the next code-block. The manual addition of the new item is necessary because the properties.ListItemId within ItemAdding is 0. But we need this ID to build the destination url in the form e.g. http://hostname/web/listname/DispForm.aspx?ID=22

public class CustomEventReceiver : SPItemEventReceiver
{
    // Code omitted ... see above
 
    public override void ItemAdding (SPItemEventProperties properties)
    {
        // Get a "reference" to the list
        SPSite siteColl = new SPSite (properties.SiteId);
        SPWeb site = siteColl.OpenWeb (properties.RelativeWebUrl)
        SPList list = site.Lists[properties.ListId];
 
        // Add the item and fill it with the values from properties
        DisableEventFiring ();
        SPListItem itemToAdd = list.Items.Add ();
        // ... 
        EnableEventFiring ();
 
        // Cleanup
        site.Dispose ();
        siteColl.Dispose ();
 
        // Redirect
        SPUtility.Redirect (targetUrlOfNewItem,
            SPRedirectFlags.Default, _currentContext);
    }
}

Hint: Because the form used to display an item can change you should retrieve it via the SPList.Forms[PAGETYPE.PAGE_DISPLAYFORM]-property.

Conclusion

The HttpContext.Current is only available within the constructor of your event receiver when handling synchronous events like ItemAdding, ItemUpdating.

46 thoughts on “Redirecting from NewForm.aspx to DispForm.aspx after creating a new item”

  1. Where do you get the ID of the new listItem? I manually set all the properties from the properties.afterproperties colletion. However the ID = null in beforeproperties, afterproperties, and listItem.

  2. I figured out my problem. I was calling list.update() instead of itemToAdd.update(). After the update it now has the ID. Thanks for the great info, it worked like a charm.

  3. Great solution. And here’s an alternative “work-around” for this problem:

    1) Create a custom ASPX page wich in it’s OnLoad event finds the latest record posted by signed in user and redirects the user to the display page of this record.

    2) On the NewForm.aspx insert a javascript replaces the “SOURCE=” querystring URL parameter with the URL to the custom aspx page that you just created.

    Or, instead of using javascript, if you prefere, modify the link of the “New” button with the SOURCE=”my custom aspx page”

  4. Hi Eric

    I’ve tried what you describe without getting the context object. It is null even though I get it from the default constructor.
    Is there anything else one have to do to get this to work?

  5. Great Post! I am assuming that you put this code into a feature? I am relatively new to customizing sharepoint and am not sure where the code you have in this post would go?
    Thanks!

  6. Hi Eric!
    This is what I’m looking for!
    But, I can’t get it to work :(
    Could you please elaborate on how to fill “itemToAdd” with values? properties.ListItem is null, so I can’t get the values from there…
    Thank you!

  7. @Elin B
    The HttpContext is only available within the constructor when a synchronous event is fired!

    @Jen
    Yes, this can be put into a Feature. Take a look at the example in the MSDN-Library

    @Hans Erik Storeide
    You need to evaluate the SPItemEventProperties which are available inside the Event-Method. Take a look at the example in the MSDN-Library

  8. After getting some sleep, I woke up with the solution almost ready: AfterProperties. I did some more searching and ended up doing this:

    DisableEventFiring ();
    SPListItem itemToAdd = list.Items.Add();
    foreach (SPField field in itemToAdd.Fields)
    {
    if (!field.Hidden && !field.ReadOnlyField && field != null && field.InternalName != “Attachments”)
    {
    itemToAdd[field.InternalName] = properties.AfterProperties[field.InternalName];
    }
    }
    itemToAdd.Update();
    EnableEventFiring ();

    However; I still get “Thread was being aborted” in the Event Log (although the redirect works just fine). Is it possible to get rid of the event or do I just have to live with it?

  9. @Hans Erik Storeide
    I used a similar solution to add the new item.
    The “Thread was being aborted”-exception comes from ASP.Net when ending or redirecting a response. Take a look at the KB-Article to solve this problem.

  10. I’m doing the redirect in the ItemUpdating and need to update the item with the updated values. I’m doing it by itemToAdd[field.InternalName] = properties.AfterProperties[field.InternalName]; as described above, but somehow all the values (except Title and a custom field) that was set in ItemAdding by SP, is gone after checking in. I’ve tried to explicately setting the “Title”, but then the properties.AfterProperties[field.InternalName] value is null.

    else if (field.InternalName.Equals(“Title”))
    {
    listItem[field.InternalName] = properties.AfterProperties[field.InternalName];
    }

    I’ve also tried to test on SPItemEventDataCollection.ChangedProperties.count, but this seems to always be 0.

    Any ideas?

  11. I tried this code.Works fine when aluse for all the fields in alist are entered.In case of a list which doesn’t have mandatory fields,this code fails,when those field values are not entered.Any idea on how to fix this??

  12. I’m using the Announcements list in which the Date field is not mandatory.So when I try to add a new announcement, leaving the date field empty, redirection does not happen.How to overcome this?

  13. @meera

    This code is more a hint than a complete working snippet. You need to fetch e.g FormatExceptions or DateTime-Parse-Exceptions (when the date is not set) while setting the value of a field. ;-)

  14. That’s understood.I appreciate your work. But the code presumes that all values are entered.There are situations, when one cannot make every field in a list mandatory.Do u have any idea as to how the code can be modified to work in such a situation?I tried checking if the itemToAdd[field]=null,which was in vain.I couldn’t find a better person to suggest a solution.
    Anywayz thanx..

  15. I’m also trying to redirect inside the updating method after update the item. But it is not possible. Can you post the example for it ?

  16. The problem with this solution is that if there is an attachment, AttachmentAdding will never be called because the redirect will cause all the other events to be bypassed. As far as I can see, there is no way of knowing if you have an attachment in ItemAdding/ItemUpdating so you can’t conditionaly delay redirection until have the attachment has been added. I haven’t found a way around this. It would be nice if MS had given us a way to specify the redirect url in the SPItemEventProperties so that SharePoint could do the redirect after all processing has been done on the item.

  17. @C Jensen

    Yes, that is right. This code also causes trouble if you want to add an item to the list via code. The event-handler “is called” and the tread is being aborted …

    The only solution here would be to add the attachment on your own.

    I improved the code to check if the http-request is coming form the “create-form” of the current list. If not the list-item-creation was done through code and a redirect will not take place.

    I hope there will be an improvement in a future service pack …

  18. Thank you very much for this example! I will have to try this. It would have been interesting so hear from “Hans Erik Storeide” what the problem was (and the solution).

  19. I have an attachments in my item, so can you give me an example to add an attachments to item?

    Thank you very much!

  20. Hey guys,

    as you all see the “redirecting-issue” is not that easy to solve.

    1. If you use attachments you have to manually add them to SharePoint because the redirection will abort the thread.

    2. The aborted thread can be a problem if you add items through code (you are not “coming” from your NewForm.aspx”) because your code after the adding of the item will not be executed.

    3. After using Reflector I found out that the SPFormContext has a property named RedirectUrl. Unfortunately this member is internal and cannot be accessed. Further investigation revealed that this property internally (through SPUtility.DetermineRedirectUrl SPUtility.GetRedirectUrl) uses the querystring-paramater named “Source”. You all have seen it :-)

    So there is no real solution for this issue as you cannot change the querystring paramter afterwards. I will further investigate this problem …

  21. Hi! Eric

    A very interesting article you have here. I am able to successfully re-direct to a different page in ItemAdding synchronous event.

    Your solution, which by the way is awesome, solves part of my problem.

    In an ItemAdding event when I re-direct to a different page, SharePoint throws following error message:

    “{Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.}” -{System.Threading.ThreadAbortException}

    As a result of which ItemAdded event does not fire. Additional processing which needs to be done in ItemAdded event does not execute.

    Any help on this will be much appreciated. I have been trying to solve this problem over 2 weeks now.

    Cheers,
    Sandeep

  22. @Sandeep
    Sorry, I haven’t had this kind of error. Hard to tell whats the cause of this problem.

    I hope you get it solved!

  23. Hi Eric:
    First of all, thanks for your excellent post. It has help me a lot in doing a feature for redirecting to NewForm.aspx and DispForm.aspx.
    I have made a post in spanish in our site, explaining how it can be done, extending on your source code, and explaining the different steps, and I think that I have also found the solution to the problem with the Attachments.
    The post is in this link:
    http://www.ciin.es/sites/blog/Lists/Entradas%20de%20blog/Post.aspx?ID=480

    I have solved the problem with the Attachments using the following code:

    if (field.InternalName == “Attachments”)
    {
    //Here we obtain the collection of attachments from the Request object
    HttpFileCollection miHFileCol = _currentcontext.Request.Files;
    if (miHFileCol.Count != 0)
    {
    foreach (String key in miHFileCol.AllKeys)
    {

    // We get the InputStream form each attachment and save it on a byte array
    int numBytesToRead = miHFileCol[key].ContentLength;
    if (numBytesToRead > 0 && (!String.IsNullOrEmpty(miHFileCol[key].FileName)))
    {
    string fileName = Path.GetFileName(miHFileCol[key].FileName);
    byte[] input = new byte[numBytesToRead];
    Stream miStream = miHFileCol[key].InputStream;
    miStream.Seek(0, SeekOrigin.Begin); //This line is very important
    int numBytesRead = 0;
    while (numBytesToRead > 0)
    {
    int n = miStream.Read(input, numBytesRead, numBytesToRead);
    if (n == 0)
    break;
    numBytesRead += n;
    numBytesToRead -= n;
    }
    miStream.Close();
    // We add the file as an attachment to the item
    itemToAdd.Attachments.Add(fileName, input);
    }
    }
    }
    }

    I hope this helps you for the case you want to keep using attachments using this feature.
    Cheers,
    Angel

  24. Thanks for the post, I don’t quite get where this goes though. Appears to be traditional C# however it doesn’t work if added to the NewForm.aspx but I don’t have VS 2005 I only have VS2003 so I can’t create my own lists. Could you help clarify where in the site this should go.

    I have gotten a ddwrt:GenFireServerEvent(concat(‘__commit;__redirect={‘,$RedirectLoc,’}’)) style redirect working, however if I don’t know my ID yet I can’t tell it where to go. Thanks for the articles about Sharepoint.

  25. actually great job ,but there is a very easy way to accomplish the same approach using Share Point designer ,just edit the newform.aspx page and replace the behavior of the OK and Cancel button ,the default behavior is to commit and redirect to Allitems.aspx ,just replace it with commit and redirect to Dispform.aspx with any query string parameter you want .
    i’v try your code but HttpContext.Current always null even inside the constructor?? any idea ?????

  26. @iyad
    Yes, I’m know this is possible but I really dislike “solutions” where the SharePoint Designer is involved :-)

  27. Hi iyad,

    tried the same like you in SPD, however, when I add the parameter ?ID={@ID} to the link, sharepoint does not take the correct ID of the current item, but {@ID} appears in the link. Does anyone know how to overcome this?

  28. Hi Christiane,

    can you provide your xslt-code from your DataFormWebPart?

    What is a problem is the fact that when you are “inside” NewForm.aspx the new id of the item is not set. After submitting the form the item gets created and an id is assigned.

  29. Hi Eric,

    thank you for the quick response. The button I added has this code:

    I thought that when I add the “commit”-Action before the “Redirect”-Action, the ID is already set?

  30. javascript: {ddwrt:GenFireServerEvent(‘__commit;__refresh;__redirect={../../Workflows/WorkflowA/Workflow 1.aspx?List_x003D_e62e146c-f505-4a05-851b-d45bf9713c1d&ID_x003D__x007B_@ID_x007D_&TemplateID_x003D__x007B_62e83e8c-883a-4f89-9f7b-65b32976557a_x007D_&Source_x003D_https%3A%2F%2Ferlh10wa%2Eww005%2Esiemens%2Enet%2Fsites%2Feshs%2Fpublic%2Fschulung%2Fchristiane%2Fdefault%2Easpx}’)}

  31. In my jQuery Library for SharePoint Web Services ( http://spservices.codeplex.com ), I’ve implemented this in an assembly-free way using jQuery and SharePoint’s Web Services. Simply drop a simple function call into the page, and the redirection happens, like magic!

    M.

    Sorry – poor typing in my previous comment for the links.

  32. Hi everyone.

    I feel obliged to post my solution on this site since I’ve had a very similar problem to all of you and this site helped me out.

    I had to redirect a user after they uploaded and captured the metadata of a document. I also therefore struggled with the lack of context vs not having the metadata saved.

    After the file has been uploaded, SharePoint assigns a SPListItem to it and then you are typically redirected to a CRUD screen where you capture this metadata. This got me thinking that I should try to get hold of ‘THAT’ item and add to it’s metadata instead of trying to add a new list item. This will therefore be un intercept of the update, never the add.

    Here is the snippet (sorry for the mess but this window is a little small to copy code)

    public override void ItemUpdating(SPItemEventProperties properties)
    {
    SPSite siteColl = null;
    SPWeb site = null;
    string url = null;
    bool hasError = true;
    try
    {

    siteColl = new SPSite(properties.SiteId);
    site = siteColl.OpenWeb(properties.RelativeWebUrl);
    SPList list = site.Lists[properties.ListId];

    SPFile file = properties.ListItem.File;

    // Add the item and fill it with the values from properties
    DisableEventFiring();

    //Note that the ‘reserved’ columns in SharePoint uses the ‘vti_’ prefix for it’s property attributes

    file.Item[BusinessObjects.Entities.KnowledgeBaseArticle.ARTICLE_TITLE] = properties.AfterProperties[“vti_title”];
    file.Item[BusinessObjects.Entities.KnowledgeBaseArticle.CATEGORY] = properties.AfterProperties[“Category”];
    file.Item[BusinessObjects.Entities.KnowledgeBaseArticle.RELATED_TO] = properties.AfterProperties[“RelatedTo”];
    file.Item[BusinessObjects.Entities.KnowledgeBaseArticle.DESCRIPTION] = properties.AfterProperties[“vti_description”];
    file.Item[BusinessObjects.Entities.KnowledgeBaseArticle.RATING_VALUE] = properties.AfterProperties[“RatingValue”];
    file.Item.Update();
    file.CheckIn(null);
    hasError = false;

    //build url
    url = GetRelativeURL(properties.ListTitle, file.Item.ID);
    }
    catch (Exception ex)
    {
    Helper.EventLogHelper.LogException(ex);
    hasError = true;
    }
    finally
    {
    EnableEventFiring();
    // Cleanup
    site.Dispose();
    siteColl.Dispose();
    }
    if (hasError == false)
    {
    // Redirect
    SPUtility.Redirect(url, SPRedirectFlags.RelativeToLayoutsPage, _currentContext);
    }
    }

    I hope that this code can help someone as the result for me was that that file was uploaded successfully and that the metadata that I needed was indeed captured.

    Kind regards
    Michael

  33. This solution doesn’t work in SharePoint 2010. The EnableEventFiring and DisableEventFiring funciton no more exist. There properties to do similar thing. I’ve used those but without any success. Any thought on how to do the redirect fro synchronous event handler?

Comments are closed.