Webforms to MVC: Handling the User Control Transition

Wednesday Nov 19th 2014 by Peter Shaw
Share:

Explore the differences that webforms programmers moving to MVC might encounter in user controls.

Many of the newer breed of .NET developers have entered a world where they only know ASP.NET MVC as a way of developing complex web applications; there are, however, still a great many of the slightly older generation out there who are stuck in Webforms land. This generation is being dragged kicking and screaming towards ASP.NET MVC and the biggest overwhelming question I see consistently get asked in the forums and news groups is

Where the heck are my user controls?

First, a Very Brief Webforms Refresher

For those readers who have never been exposed to webforms, this was Microsoft's first attempt at creating a rapid web development paradigm in previous versions of Visual Studio and IIS. The idea behind webforms was to make the development model as close as possible to the existing Winforms desktop development one, so that developers currently working on desktop applications could make the translation to writing web apps as smooth and as fluid as possible.

The idea worked, and had immense success. Almost overnight, an army of early Windows developers who knew nothing about creating applications for the web became efficient web developers, and life was good.

Webforms worked because, if you choose to, you would never have to write a line of code ever again. It was absolutely possible to create an entire application by doing nothing more than dragging text boxes and controls from a tool kit onto your web page. These components then could be linked together by setting properties that allowed things to just work.

You could drop databases in, XML files, even components that would create site navigation for you automatically based on the pages you created. Oh, and let's not forget master pages: Create a template, mark sections to be filled in with your own content, and Bob's your uncle. All of this was powered by something called the Postback model and the ASP.NET Page lifetime cycle.

So, Why Did We Abandon It in Favour of MVC?

Although the model was a good one, it had many flaws. Postback, for example, was actually quite a slow process. Then there where all the various methods of keeping state, such as the Session and ViewState models. If you got ViewState wrong, for example, it wasn't impossible to make the amount of data your web server had to return to the client be twice the size of the actual content in the page. If you wanted to do anything even moderately different to the subscribed way of doing things, you had to understand the page lifetime cycle implicitly and in great detail.

As businesses became more and more web aware, they wanted development times shortened and made less costly, they wanted agile processes that meant they could continuously ship, and it was clear that WebForms was starting to fall behind.

Introducing MVC

In late 2007, Microsoft introduced a new development model on the world, one intended to replace webforms as the mainstream web development platform. Initially, uptake was very slow, and it wasn't until about version 3 that things really started to take off. Today, we're now at version 5 with a bright new future going forward into Microsoft's vNext strategy, which actually promises to bring some elements of webforms back into play.

On the whole of it all, MVC is now the platform of choice, but there are still a great many businesses out there who still have WebForms based business solutions in play. For the most part, these solutions work well for them, and many of them see little need to upgrade. There is, however, always a need to add new parts onto these applications, or expand them to do something they were never intended to do.

In many cases, the developers working with these applications have more or less gotten to grips with the difference between page-behind code and controller-based code, but many of them still have not gotten to grips with the loss of drag & drop components, especially those who have libraries and libraries of user-created component libraries for specialist tasks.

So, Can MVC Do 'User Controls'?

The answer to that is both a yes and a no.

First, let's start with the no.

MVC Does not have the concept of a tool panel like webforms does, so the ability to drag controls from a toolbox onto your web page and wire them together with properties is long gone. MVC also does not understand things like ViewState or page postback, so the common pattern of having a button call a single function in your page code and then respond to that depending on the stage in the page lifecycle has also vanished. Instead, these two scenarios have been replaced by "HTML Partials" and "Controller Actions"

In MVC, you have a controller method. This method is responsible for drawing the entire page, providing its data and is specific to a particular http request such as POST or GET.

In webforms, when a button was clicked, this click immediately called back into a method in a single class. This method had the ability to change state/data and indeed ANY exposed property available within the web page the button was contained on. This was because the entire page was a single class, and like any class any properties, objects, or data contained within where instantly accessible to all elements of that class. Because MVC treats each request as a completely new request, it's not possible to directly manipulate the page contents from C# or VB any longer.

Moving onto the yes, however...

Microsoft realised it they would need some method of allowing controls to be added to a page, and a method to allow those controls to interact with the page in a not too dissimilar a manner to the webforms' postback model.

To address this need, Microsoft used a controller-based model, where clicks on buttons and other elements were handled by client-side libraries such as JQuery. Now, instead of adding a button, and being able to double-click it to create code to handle it, you now need to use JavaScript to make either an Ajax-based partial call of a full page request using something like an anchor tag.

Using this method meant that each individual button could be given its own dedicated slice of code in the controller, and, if done correctly, you could emulate the postback model using partial post requests to suitably marked methods in the controller class.

A Simple Example

Based on what was said above, the following WebForms code:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"
   CodeBehind="Default.aspx.cs"
   Inherits="webui.Default"%>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title></title>
</head>
<body>
   <form id="form1" runat="server">
   <div>
      <asp:Button ID="btnTestButton3"
         runat="server" OnClick="btnTestButton_Click"
         Text="Click me" />
      <asp:Label ID="lblTestLabel"
         runat="server"
         text="I'm a label. How do you do?" />
   </div>
   </form>
</body>
</html>

Default.aspx.cs

using System;

namespace webui
{
   public partial class Default : System.Web.UI.Page
   {
      protected void Page_Load(object sender, EventArgs e)
      {
      }

      protected void btnTestButton_Click(object sender,
         EventArgs e)
      {
         lblTestLabel.Text = "I'm a label. You changed
            me when you clicked the button."
      }
   }
}

would be equivalent to the following MVC code:

Index.cshtml

<!DOCTYPE html>

<html>
<head>
   <title>Index</title>
   <script src="scripts/jquery.js"></script>
   <script type="text/JavaScript">

      $(document).ready(function(){
         $('#btnTestButton').on('click', function(){
            $.get("@Url.Action("Button","Home")");
         });
      });

   </script>
</head>
<body>
   <div>

      <button id="btnTestButton3">Click me</button>
      <p id="lblTestLabel">@Viewbag.TestLabel</p>

   </div>
</body>
</html>

IndexController.cs

using System.Web.Mvc;

namespace webui.Controllers
{
   public class HomeController : Controller
   {
      public ActionResult Index()
      {
         ViewBag.TestLabel = "I'm a label. How do you do?";
         return View();
      }

      public ActionResult Button()
      {
         ViewBag.TestLabel = "I'm a label. You changed
            me when you clicked the button.";
         return View("index");
      }

   }
}

You can see straight away that the click requires two methods to handle it. Think of the first part as your page load, and the second part as your click handler. I just used a simple get request above, but you can post, put, head, or any other HTTP verb you fancy using. You would quite often also typically use one 'cshtml' file per method rather than share them as I have here.

I've deliberately kept the example as simple as possible to illustrate the key concepts. You now should have a slightly better mental picture of how MVC responds to actions in your HTML compared to how Webforms does.

So, What About User Controls?

Finally, we get to the part of this article that everything has been leading up to. Just as with Postback, there is a replacement in MVC for User controls; this replacement is called "Html Partials"

As you saw previously, a controller is linked to an HTML page. These HTML pages are known in MVC speak as "Views". A view is composed by the MVC engine when a page is requested via a controller, and then that view is presented either as a whole HTML page or as just a partial fragment of HTML data.

I'm not going to go into the full page model, but this composition is made up from "Layouts" that are the MVC equivalent of "Webforms Master Pages" and the regular views which are the same as Webforms pages that only have ASP:PlaceHolders in them marking sections where your code should be inserted.

If you look at the folder structure of a typical MVC project in Visual Studio, you should see something like the following:

Webforms
Figure 1: The folder structure in Visual Studio

In the controllers folder, you have a 'HomeController' class. This class has three methods: 'Index', 'About', and 'Contact'. Each of these three methods has a corresponding view in the "Views->Home" folder. MVC will automatically look for a view that matches a method name when composing a view, so by browsing to:

/home/index

ASP.NET will automatically find the file index.cshtml within the correct folder. Then, it will use that to compose the page to return.

In our case, you'll see that we have a _layout.cshtml in the "Shared" folder (and the appropriate commands in the views to use it), so ASP.NET MVC will automatically combine this layout, with the index file to produce the page. If you look in the shared folder, however, you'll also see that there is a file called '_LoginPartial.cshtml'; this is a further snippet of HTML code with MVC processing directives in that can be reused and inserted into a page at any time.

In our case, if you open the _Layout file, you'll see the following lines:

<body>
   <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
         <div class="navbar-header">
            <button type="button" class="navbar-toggle"
               data-toggle="collapse"
               data-target=".navbar-collapse">
               <span class="icon-bar"></span>
               <span class="icon-bar"></span>
               <span class="icon-bar"></span>
            </button>
            @Html.ActionLink("Application name", "Index", "Home",
               null, new { @class = "navbar-brand" })
         </div>
         <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
               <li>@Html.ActionLink("Home", "Index", "Home")</li>
               <li>@Html.ActionLink("About", "About", "Home")</li>
               <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
            </ul>
            @Html.Partial("_LoginPartial")
         </div>
      </div>
   </div>

This '@Html.Partial' MVC Razor directive will insert ANY content it finds in that _LoginPartial file into your output page at the point where it appears.

If we take a look at _LoginPartial, you'll see it's no different to the other view files, except for the fact it lacks the various processing directives to set things like the Layout.

@using Microsoft.AspNet.Identity
@if (Request.IsAuthenticated)
{
   using (Html.BeginForm("LogOff", "Account", FormMethod.Post,
      new { id = "logoutForm", @class = "navbar-right" }))
   {
   @Html.AntiForgeryToken()

   <ul class="nav navbar-nav navbar-right">
      <li>
         @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!",
            "Manage", "Account", routeValues: null,
            htmlAttributes: new { title = "Manage" })
      </li>
      <li><a href="javascript:document.getElementById('logoutForm')
         .submit()">Log off</a></li>
   </ul>
   }
}
else
{
   <ul class="nav navbar-nav navbar-right">
      <li>@Html.ActionLink("Register", "Register", "Account",
         routeValues: null, htmlAttributes: new
         { id = "registerLink" })</li>
      <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null,
         htmlAttributes: new { id = "loginLink" })</li>
   </ul>
}

Just like a user control in WebForms, the main part that gets included in your document is nothing more than ASP.NET mark up (in this case, using the Razor syntax). The code to drive it, however, will usually remain in the main controller where you wanth to use it.

And that's pretty much all there is to the very basics of moving from the user control model of WebForms to ASP.NET MVC.

If you want your partials to have their own controller classes behind them, then that is also possible. MVC provides HTML methods that allow you to insert a partial directly from a controller action if that's what you need to do; however, the recommended way of providing interactivity on an HTML Partial level is to build light weight controllers that receive and return JSON data.

Doing things this way means that, once the page is composed, you only ever need to do a full reload if a full page call is requested, for the rest (such as dynamic data updates) you can simply use JavaScript code inside the rendered view to grab the data and update only the parts needed.

Moving from WebForms to MVC need not be difficult, but if you're firmly entrenched in the WebForms way of doing things it can seem like an uphill struggle at times. Once you get past that initial hurdle of understanding how the MVC model relates to the WebForms model, the difference can be quite intoxicating. Pretty soon, all you'll recall is some strange way you used to develop web applications.

Want me to write about something that's confusing you, or do you have some tips & tricks you'd like to share? Let me know in the comments below, of come hunt me down on Twitter (@shawty_ds) and I'll see what we can do.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved