Forms and WebForms

So you have upgraded your WebForms site to EPiServer 9 because your customer would love to use the new Forms module. Of course you can’t as the new Forms module does not support WebForms, which yo may have overlooked, as I did. I personally don’t understand why it’s not supported, as the CMS still supports WebForms.

Rewriting the whole site to MVC is also not an option. The option to rewrite all form element controls to usercontrols, creating handlers for the logic in the controllers and other solutions offered by support is also not an option I wanted to explore, with each update you would have to check and maybe rewrite the whole thing.

Google to the rescue. I found something on StackOverflow which seemed to be able to do the trick. So I did a POC and everything seems to work, I have not tested all possible options within the forms module yet, but it’s looking good. Forms are submitted, I can see the data.

You will need a few things:

First of all a helper, see this post for what’s done here

public static class MvcUtility
    {
        private static readonly ILogger Log = LogManager.GetLogger();

        // Render a partial view
        public static void RenderPartial(string partialViewName, object model)
        {
            if (model == null)
            {
                return;
            }

            try
            {
                HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
                RouteData routeData = new RouteData();

                routeData.Values.Add("controller", "Dummy");

                ControllerContext controllerContext =
                    new ControllerContext(new RequestContext(httpContextBase, routeData), new DummyController());

                IView view = FindPartialView(controllerContext, partialViewName);

                ViewContext viewContext = new ViewContext(
                    controllerContext,
                    view,
                    new ViewDataDictionary { Model = model },
                    new TempDataDictionary(),
                    httpContextBase.Response.Output);

                view.Render(viewContext, httpContextBase.Response.Output);
            }
            catch (InvalidOperationException invalidOperationException)
            {
                Log.Critical("Cannot render partial view for: {0} \r\n {1}", partialViewName, invalidOperationException);
            }
            catch (NotImplementedException notImplementedException)
            {
                Log.Critical("Cannot render partial view for: {0} \r\n {1}", partialViewName, notImplementedException);
            }
            catch (ArgumentNullException argumentNullException)
            {
                Log.Critical("Cannot render partial view for: {0} \r\n {1}", partialViewName, argumentNullException);
            }
        }

        // Find the view, if not throw an exception
        private static IView FindPartialView(ControllerContext controllerContext, string partialViewName)
        {
            ViewEngineResult result = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);

            if (result.View != null)
            {
                return result.View;
            }

            StringBuilder locationsText = new StringBuilder();

            foreach (string location in result.SearchedLocations)
            {
                locationsText.AppendLine();
                locationsText.Append(location);
            }

            throw new InvalidOperationException(
                string.Format("Partial view {0} not found. Locations Searched: {1}", partialViewName, locationsText));
        }
    }

The helper uses a dummy controller with an empty view

public class DummyController : Controller
    {
        public ActionResult Index()
        {
            // Just create an empty Razor view called PartialRender.cshtml in views\shared
            return this.PartialView("PartialRender");
        }
    }

Next I created a partial view for a ContentArea e.g. FormsContentArea.cshtml, which does nothing more than displaying the model

@using EPiServer.Core

@model ContentArea

@Html.DisplayForModel()

On your block e.g. you can render the form as follows

<% MvcUtility.RenderPartial("FormsContentArea", this.CurrentBlock.FormContentArea); %>

Next you can create a (wrapper)Block or Page where you add a ContentArea specific for FormContainers. This way the editing experience was not very good though, as there still was no renderer for the form itself.

[Display(
            GroupName = SystemTabNames.Content,
            Order = 320)]
[CultureSpecific]
 [AllowedTypes(typeof(FormContainerBlock))]
public virtual ContentArea FormContentArea { get; set; }

So I tried someting else. I created a UserControl specifically for the FormContainerBlock, as you cannot render the block directly, you will have to wrap it in a ContentArea

public partial class FormContainerBlockControl : BlockControlBase<FormContainerBlock>
    {
        protected ContentArea FakeArea { get; set; }
        protected void Page_Load(object sender, EventArgs e)
        {
            ContentArea contentArea = new ContentArea();
            contentArea.Items.Add(new ContentAreaItem
            {
                ContentLink = this.CurrentBlock.Content.ContentLink
            });

            this.FakeArea = contentArea;
        }
    }

And render it like before, which will give the full editing experience

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FormContainerBlockControl.ascx.cs" Inherits="EPiServerWebForms.Views.Blocks.FormContainerBlockControl" %>
<%@ Import Namespace="EPiServerWebForms.Business.MvcHelpers" %>
<% MvcUtility.RenderPartial("FormsContentArea", this.FakeArea); %>

Hope this helps a bit. As I said it’s a POC, it’s not optimal and needs some extensive testing for all ins and outs of the module, but still wanted to share now, because I bet I’m not the only one with this issue…. There’s also a gist with all the bits

The POC continues in pt2 and pt3

One thought on “Forms and WebForms

Leave a comment