Friday, September 20, 2013

MVC with ASP.NET Web Forms

For those of us who have been doing development for a little while, we are used to the coming and going of fads. Especially so, we are used to Microsoft putting age old design patterns on things and marketing them as the next big thing. These days I am always being asked by .NET developers: "Do you know ASP.NET MVC?" I always respond, "I have always used MVC in my ASP.NET applications so yes." Seriously, what is the big deal? Wasn't the point of ASP.NET Web Forms to streamline the development process? Wasn't it replacing existing MVC frameworks of its day? Now all of the sudden, everyone is screaming about how slick ASP.NET MVC is despite the fact that some of the most useful features of ASP.NET Web Forms, like data bound controls, wired event handling etc... aren't even available.

So just to clear the air, MVC has always been easy with traditional ASP.NET.  There is no need to abandon something that works really well just so Microsoft can tell you what you can and can't do. Don't buy into the hype. If you are into ASP.NET MVC, by all means keep using it. But, if you have been using Web Forms for a while and you like it, don't let some architecture astronaut sell you some bill of goods about how Microsoft is bringing something new to the game when they are just forcing you to practice good design to begin with by removing flexibility from your arsenal.

So without further adieu, here is one of the many ways that you can use the MVC pattern in classic ASP.NET using the example of a simple Web Forms login page:

First, create your model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using [MyStuff].Common.Utils.Localization;
 
namespace [MyStuff].Website.Login
{
  public class LoginModel
  {
    LocalizableMessageStorage messages;
 
    public LoginModel(LocalizableMessageStorage messageStore)
    {
      messages = messageStore;
    }
 
    public string PageTitle
    {
      get { return messages.GetLocalizedMessage(Thread.CurrentThread.CurrentUICulture, 
                                                  "lp_login_message"); }
    }
 
    public string UserLabel
    {
      get { return messages.GetLocalizedMessage(Thread.CurrentThread.CurrentUICulture, 
                                                  "lp_username_message"); }
    }
 
    public string PasswordLabel
    {
      get { return messages.GetLocalizedMessage(Thread.CurrentThread.CurrentUICulture, 
                                                  "lp_password_message"); }
    }
 
    public string UserName
    {
      get;
      set;
    }
 
    public string Password
    {
      get;
      set;
    }
  }
}

Next, let's create our controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using [MyStuff].Common.Utils.Localization;
 
namespace [MyStuff].Website.Login
{
  public partial class Login : System.Web.UI.Page
  {
    private LoginModel loginModel;
 
    public LoginModel PageModel
    {
      get { return loginModel; }
    }   
 
    protected void Page_Load(object senderEventArgs e)
    {
      if(!IsPostBack)
      {
        LocalizableMessageStorage messages = 
          (LocalizableMessageStorage)Application["LocaliazedMessages"];
        Session["LoginModel"= new LoginModel(messages);
      }  
      
      loginModel = (LoginModel)Session["LoginModel"];
 
      Page.DataBind();    
    }
 
    protected void btnLogin_Click(object senderEventArgs e)
    {
      loginModel.UserName = txtUserName.Text;
      loginModel.Password = txtPassword.Text;
 
      //yada yada yada
    }
  }

Then our view. Don't judge my ugly html, it is just a demonstration :=)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="[MyStuff].Website.Login.Login" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="login" runat="server">
    <div>
      <asp:Label ID="lblTitle" runat="server" Text="<%#PageModel.PageTitle %>"></asp:Label>
      <asp:Label ID="lblUser" runat="server" Text="<%#PageModel.UserLabel %>"></asp:Label>
      <asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
      <asp:Label ID="lblPassword" runat="server" Text="<%#PageModel.PasswordLabel %>">
      </asp:Label>
      <asp:TextBox ID="txtPassword" runat="server"></asp:TextBox>
      <asp:Button ID="btnLogin" runat="server" OnClick="btnLogin_Click" />
    </div>
    </form>
</body>
</html>

Look familiar? And it is true to the MVC pattern. Now get off my back Microsoft!