Clean Shave: Razor Pages for Web Forms Developers

If you’re an ASP.NET Web Forms developer and you’re unsure of how to take your skills to the future of the .NET platform (e.g., .NET Core or .NET 6), there’s hope. Although Microsoft won’t port Web Forms, you can apply your existing skillset to a similar framework, called Razor Pages. You’ll still need to think about this new platform differently, but if you take the time, I think you’ll find a similarly powerful and easy to use tool to create web applications.

Where You’re Coming From

Back in the early 2000s, I was a C++ developer and was one of these “you’ll have to take my pointers out of my cold dead hand” guys. But once I was introduced to how garbage collection worked in .NET, I was a convert. In those early days, I was writing ASP.NET (after my time writing components for ASP projects).

The reality was that I didn’t understand how the web actually worked, but I was tasked with creating websites and web apps using ASP.NET. Microsoft came to my rescue by introducing Web Forms. Nowadays, Web Forms gets quite a lot of hate from many directions about how un-web-like it was. But it helped people like me dip my toe in the web world without the fear that comes from something new. Microsoft successfully turned desktop developers into web developers. But it wasn’t without inherent risks.

Web Forms introduced drag-n-drop designing to web development. Under the covers, it was trying to hide the details of the web and feel like the server-side code was something akin to a stateful development solution. Add in ViewState and Session State, and lots of developers were able to accomplish a lot of value for their companies and employers.

But it’s now 2023. We’ve been through a world of change since those early days. For many Web Forms developers, it can be overwhelming to be asked to learn JavaScript on the client, separate concerns into Controllers and Views, and write code that is truly stateless. But that’s where we are now. There isn’t a perfect upgrade path to ASP.NET Core for Web Forms developers. But there are some ways to apply our existing knowledge without throwing out the baby with the bathwater. In comes Razor Pages.

Introducing Razor Pages

As an answer to Web Pages, Microsoft introduced ASP.NET MVC as a Model-View-Controller framework that separated (and simplified testability) views and logic. This has been the prevailing framework for many projects, although it never did replace Web Forms. After .NET Core was introduced, Razor Pages was introduced to have a model closer to a page-by-page solution instead of complete separation. Now with Blazor, another solution has been added to the quiver of tools. For this article, I’m going to focus on Razor Pages themselves as I think it’s the most straightforward migration path for Web Forms developers.

… this article is continued online. Click here to continue.

Page is Dirty using JavaScript

In the bad old days of desktop applications, every form object in the world had a Dirty property that let you easily check to see if the user had made any changes to the data on the form. It’s almost as easy with client-side code running in the Web browser, provided you use jQuery. This line finds every input tag and ties the tag’s change event to a JavaScript function called flagChanges:

$("input").change(function () 
            {
              flagChanges();
            });

Read more in Visual Studio Magazine;

Other References

https://www.c-sharpcorner.com/blogs/page-is-dirty-using-javascript1

Reading XmlDocument fragment

I would like to loop through following collection of authors and for each author  retrieve its first and last name and put them in a variable strFirst and  strLast?

<Authors>  
  <Author>  
    <FirstName>Jon</FirstName>  
    <LastName>Doe</LastName>  
  </Author>  
  <Author>  
    <FirstName>Shahzad</FirstName>  
    <LastName>Khan</LastName>  
  </Author>  
</Authors>  

We’ll use XmlDocument class to parse this XML fragment;

using System;    
using System.Xml;    
public class XMLApp    
{    
    public void YourMethod(String strFirst, String strLast)    
    {    
        // Do something with strFirst and strLast.    
        // ...    
        Console.WriteLine("{0}, {1}", strLast, strFirst);    
    }    
    public void ProcessXML(String xmlText)    
    {    
        XmlDocument _doc = new XmlDocument();    
        _doc.LoadXml(xmlText);    
        // alternately, _doc.Load( _strFilename); to read from a file.    
        XmlNodeList _fnames = _doc.GetElementsByTagName("FirstName");    
        XmlNodeList _lnames = _doc.GetElementsByTagName("LastName");    
        // I'm assuming every FirstName has a LastName in this example, your requirements may vary. //     
        for (int _i = 0; _i < _fnames.Count; ++_i)    
        {    
            YourMethod(_fnames[_i].InnerText,    
            _lnames[_i].InnerText);    
        }    
        public static void Main(String[] args)    
        {    
            XMLApp _app = new XMLApp();    
            // Passing XML text as a String, you can also use the    
            // XMLDocument::Load( ) method to read the XML from a file.    
            //    
            _app.ProcessXML(@" <Authors>    
            <Author>    
              <FirstName>John</FirstName>    
              <LastName>Doe</LastName>    
            </Author>    
            <Author>    
              <FirstName>Shahzad</FirstName>    
              <LastName>Khan</LastName>    
            </Author>    
            </Authors> ");    
        }    
    }// end XMLApp    
}  

Resources

https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmldocument?view=net-7.0

A Simple 2-Column Layout in Razor

2 column layouts are rather popular on the web, and there are 1,001 ways to make them work. The approach you choose really depends on the type of content you have, and how you want images and backgrounds to work. What I’ll show is the Razor _Layout and CSS to achieve the following look:

The Razor _Layout file can rely on partial views to handle each of the primary sections: top, navigation, sidebar, and footer. RenderBody will produce the primary content area.

<!DOCTYPE html>
<html>
<head>    
    <title>@ViewBag.Title</title>    
    <link href="@Url.Content("~/Content/Site.css")" 
          rel="stylesheet" type="text/css" />
</head>

<body>
    @Html.Partial("_top")
    @Html.Partial("_navigation")
    @Html.Partial("_sidebar")                               
    <div id="body">
        @RenderBody()      
    </div>    
    @Html.Partial("_footer")   
</body>
</html>

A quick note on Html.Partial. If any sections, like the sidebar and navigation sections, require some logic or model information to build their piece of the UI, then use Html.Action instead of Html.Partial. Html.Action allows you to setup a little sub-request inside the current request and allow a controller action to build a model and select a view.

The CSS coming up assumes each partial view will render inside an element with an id matching it’s purpose (so the _top view renders a div with an id of top).

<div id="top">
    This is the top content
</div>

Then finally, add some styles to the CSS file:

#top {
    height: 20px;
    text-align: center;
    background-color: black;
    color: white;   
}

#navigation {
    height: 25px;
    margin: 5px;
    padding: 5px;
    color:crimson;
}

#sidebar {
    margin: 5px;
    padding: 5px;
    position: absolute;
    top: 50px;
    left: 314px;
}

#body {
    width: 300px;
    padding: 5px;
    margin: 5px;
    background-color: #999999;
    border-radius: 5px;
    border: 2px black solid;
}

#footer {
    padding: 5px;
    text-align: center;
    background-color: black;
    color: white;
}

The trick is to use absolute positioning on the sidebar content, which is possible because we know the exact amount of space taken by the 2 sections at the top of the page, and we know the exact width of the content area (it is set explicitly in the CSS). You’ll probably want to give the body more space than the 300px given in the sample above (which was constrained so the screenshot would fit on this page).

Resources

https://dev.to/codeply/bootstrap-5-sidebar-examples-38pb

JSON Example

This page shows example of JSON and XML. This is a handy reference only;

{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}
The same text expressed as XML:

<!DOCTYPE glossary PUBLIC "-//OASIS//DTD DocBook V3.1//EN">
 <glossary><title>example glossary</title>
  <GlossDiv><title>S</title>
   <GlossList>
    <GlossEntry ID="SGML" SortAs="SGML">
     <GlossTerm>Standard Generalized Markup Language</GlossTerm>
     <Acronym>SGML</Acronym>
     <Abbrev>ISO 8879:1986</Abbrev>
     <GlossDef>
      <para>A meta-markup language, used to create markup
languages such as DocBook.</para>
      <GlossSeeAlso OtherTerm="GML">
      <GlossSeeAlso OtherTerm="XML">
     </GlossDef>
     <GlossSee OtherTerm="markup">
    </GlossEntry>
   </GlossList>
  </GlossDiv>
 </glossary>
{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}
The same text expressed as XML:

<menu id="file" value="File">
  <popup>
    <menuitem value="New" onclick="CreateNewDoc()" />
    <menuitem value="Open" onclick="OpenDoc()" />
    <menuitem value="Close" onclick="CloseDoc()" />
  </popup>
</menu>

Reference

https://json.org/example.html