Tag: ASP.NET

  • Using media queries in razor page files

    Using media queries in razor page files

    When hardcoding a media query into an ASP.NET Razor page file, you need to use @@ instead of @ to ensure the query is rendered correctly.

    Kudos: https://stackoverflow.com/a/6873785/75594

  • Adding typescript to ASP.NET 2 WebForms project

    Adding typescript to ASP.NET 2 WebForms project

    If you are burdened with the technical debt of an old ASP.NET 2 Webforms project, then the chances are that you might not have had the chance to stay up-to-date with JavaScript revolution. A lot has changed with JavaScript in the past few years, and keeping up is a significant effort. So what should you do?

    tl;dr

    There is no silver bullet. You’ll need to take some time out to update your skills if you want to make use of the new standards and frameworks but it is possible to start incrementally updating an older application without doing a complete rewrite.

    So what do you need to understand?…

    JavaScript Standards

    If you do not know what ES5 or ES6 are – read this blog post first.

    When you first created your ASP.NET 2 application you would have been targetting a JavaScript standard known as ES5 (you might not have even known that you were using JavaScript at the time, but it is baked into the WebForms framework for handling postbacks).

    Important point: Different browsers support different versions of the JavaScript standards. Generally speaking ES5 is considered as supported everywhere.

    If you want to check if a JS standard  is supported in a specific browser then use the compatibility tables, for example:

    If you need to use a specific feature the you can make use of the website caniuse.com, for example:

    So we now know that there are different versions of JavaScript and the more recent versions are “better” than the older versions, but also that the newer versions aren’t supported across the board.

    This means that you can’t use the later versions if you still have users on older browsers. We already know that you are supporting an ASP.NET 2 application, which means that

    • It is a legacy application, so there is a fair chance that means that it is still used by people on older browsers that don’t support ES6+.
    • You are user driven and can’t force them to upgrade – perhaps they are corporate clients and pretty much dicate the terms.

    This is where you need to learn about compilation and transpilation…

    Compilers and Transpilers

    Transpilers (or source-to-source compilers) take code in one language and convert it to another language. Fortunately for us there are plenty of compilers that can convert scripts between different versions of JavaScript. You are likely to have heard of the following:

    Transpilers

    • Babel – a transpiler best known for its ability to turn ES6 into ES5
    • Webpack – a JS transpiler and CSS transpiler (LESS/SASS) and minifier

    Which one should you use? A very opinionated area and seeing as we are developing ASP.NET then my opinion is to use the hidden third option – Visual Studio 2017. This will also transpile, bundle and minify CSS and JS. Unless you have need to use an alternative you may as well use the features within VS 2017 to keep life simple.

    Supersets

    • TypeScript – a strongly typed superset of JavaScript that compiles to JavaScript.
    • CoffeeScript – a superset of JavaScript that compiles into JavaScript

    Which one should you use? This is a very opinionated area and my opinion is that TypeScript is the way to go based on the following.

    • TypeScript is managed by Microsoft (you are using ASP.NET WebForms so this should sit well with your current tech-stack)
    • TypeScript is at the heart of Angular, so it is also embraced by Google and their tech-stack
    • TypeScript is baked in to Visual Studio and VS Code – you don’t need to use Babel
    • TypeScript is a compiler and a type checker (which Babel is not)
    • Type checking capabilities are very important for large code bases
    • TypeScript will compile to ES3, ES5, ES6, ES2016, ES2017 or ESNext

    For more information on transpilation read this blog post.

    Command line

    Pretty much all modern frameworks and libraries are used via the command line (usually referred to as CLI – Command Line Interface).

    It is a big shift in the way you normally work, but it is powerful and quick. By building CLI tooling developers are able to iterate (and deal with cross platform) much faster than if they had to supporing complex UI’s and multiple IDE’s.

    Accept it. Learn it. Love it.

    Upgrade steps

    Depending on the exact details and structure of your application your steps might vary. The following is a guide to the steps that might be involved.

    Update Visual Studio

    Whilst I love VS Code for simple web apps, testing and proof of concept, Visual Studio is still my go-to editor for WebForms.

    To get the latest support for TypeScript you will need to use the latest version of Visual Studio.

    Update the solution

    • Make sure you are starting from a firm foundation – build and check-in your code without any changes using your current IDE. This is your fallback if something goes wrong.
    • Open the project in VS2017. Certain files will be updated (solution, project etc), so you’ll need to build and test again. Do this before making any further changes.

    Install Web Essentials

    If you don’t already have it, install the Web Essentials 2017 extensions into Visual Studio. This extension will deal with the JS tranpilation that you need.

    For more information see the Web Essentials website.

    Install Typescript 2.6 SDK

    There are a few tricks getting Typescript checked / installed / updated.

    Useful commands:

    • tsc -v – what version of the TypeScript compiler is running
    • where tsc – where are the installed versions of the TypeScript compiler
    • npm install -g typescript – install / update TypeScript as a node module
    • npm upgrade -g typescript – install / update TypeScript as a node module

    TypeScript 2.6 and VS2017 will automatically infer the shared types between files – it is no longer necessary to use triple-slashreferences for types.

    See https://docs.microsoft.com/en-us/visualstudio/ide/javascript-intellisense#Auto

    After transpilation you will want to bundle your files. These can be done with a bundleconfig.json file.

    NOTE: JS files are bundled in the order they are listed, so add base classes first.

    Build, test & commit

    At this point follow your usual process to build, test and commit your changes. This is a good point to fall back to if the something goes wrong in the subsequent steps.

  • Extract URL Information

    Extract URL Information

    To get information about the currently requested URL in ASP.NET use the Request.Url class. For example to extract the information from the URL http://www.example.com/myApp/myPage.aspx?myQs=14 the Request.Url class provides the following properties:

    Request.Url = "http://www.example.com/myApp/myPage.aspx?myQs=14"
    Request.Url.AbsolutePath: "/myApp/myPage.aspx"
    Request.Url.AbsoluteUri: "http://www.example.com/myApp/myPage.aspx?myQs=14"
    Request.Url.Authority: "www.example.com"
    Request.Url.DnsSafeHost: "www.example.com"
    Request.Url.Fragment: ""
    Request.Url.Host: "www.example.com"
    Request.Url.HostNameType: Dns
    Request.Url.IsAbsoluteUri: true
    Request.Url.IsDefaultPort: true
    Request.Url.IsFile: false
    Request.Url.IsLoopback: true
    Request.Url.IsUnc: false
    Request.Url.LocalPath: "/myApp/myPage.aspx"
    Request.Url.OriginalString: "http://www.example.com:80/myApp/myPage.aspx?myQs=14"
    Request.Url.PathAndQuery: "/myApp/myPage.aspx?myQs=14"
    Request.Url.Port: 80
    Request.Url.Query: "?myQs=14"
    Request.Url.Scheme: "http"
    Request.Url.Segments: {string[3]}
    Request.Url.UserEscaped: false
    Request.Url.UserInfo: ""
  • SQL Server Compact 4.0

    Loving the medium trust capabilities for small online apps – take that! all you sucky rip off hosters!!Here Comes SQL Server Compact 4.0 | Rob Tiffany

  • Design patterns

    An excellent reference for gang of four design patterns: http://www.dofactory.com/Patterns/Patterns.aspx

  • Launch Cassini from the command line

    A simple batch file will do this:
    taskkill /F /IM WebDev.WebServer.exe
    START /D "C:\Program Files\Common Files\microsoft shared\DevServer\9.0\" /B WebDev.WebServer.EXE /port:5002 /path:"$PROJECT PATH" /vpath:"/PROJECT"

    Replace $PROJECT with the local path of your web solution.

    The VPATH value refers to the virtual folder that is used. This can be replaced with just / if your application runs from the web root.

    Once saved into a batch file, this could be dropped into your start up folder to launch on every reboot.

    Respect goes to XNeuron for the lead on this one.

  • HTML Table to ASP.NET Table

    To convert an HTML table (from a string) to an ASP.NET table:

    Start with an HTML table:

    <table style="width: 100%;">
    <tr>
    <td colspan="2">
    <fieldset title="Policy Information">
    <legend><b>Policy Information</b></legend>
    <table style="width: 100%;">
    <tr>
    <td width="140">Policy Number:</td>
    <td width="150">$$Policy Number$$</td>
    </tr>
    </table>
    </fieldset>
    </td>
    </tr>
    </table>
    

    Create a Helper class:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Xml;
    using System.Web.UI.WebControls;
    using System.Web.UI;
    using System.Drawing;&amp;lt;/pre&amp;gt;
    namespace Whatever
     {
     public static class TableHelper
     {
     public static Table m_table = new Table();
    
    public static Table CreateASPTableFromXML(XmlNode node)
     {
     Table table;
     /// fetch the root table node, this will iteratively fetch the rest.
     table = GetTableFromNode(node);
    
    return table;
     }
    
    static Table GetTableFromNode(XmlNode node)
     {
     Table table = new Table();
     table.BorderStyle = BorderStyle.Solid;
     table.BorderColor = Color.Black;
     table.BorderWidth = Unit.Pixel(1);
    
    /// create the table
     if (node.Name.ToLower() != "table")
     {
     throw new Exception(string.Format("'table' element expected, {0} found.", node.Name));
     }
    
    /// apply the attributes
     ApplyAttributesToControl
    
    (node.Attributes, ref table);// From this table element find all the child rows
     foreach (XmlNode rowNode in node.ChildNodes)
     {
     table.Rows.Add(GetRowFromNode(rowNode));
     }
    
    //TableRow row = new TableRow();
     //TableCell cell = new TableCell() { Text = "here" };
     //row.Cells.Add(cell);
    
    //table.Rows.Add(row);
    
    return table;
     }
    
    static TableRow GetRowFromNode(XmlNode node)
     {
     /// create the tableRow
     TableRow tableRow = new TableRow();
    
    if (node.Name.ToLower() != "tr")
     {
     throw new Exception(string.Format("'tr' element expected, {0} found.", node.Name));
     }
    
    /// apply the attributes
     ApplyAttributesToControl(node.Attributes, ref tableRow);
    
    // From this table row element find all the child cells
     foreach (XmlNode cellNode in node.ChildNodes)
     {
     tableRow.Cells.Add(GetCellFromNode(cellNode));
     }
    
    return tableRow;
    
    }
    
    /// Process each cell and look for content.
     static TableCell GetCellFromNode(XmlNode node)
     {
     /// create the tableRow
     TableCell tableCell = new TableCell();
    
    if (node.Name.ToLower() != "td")
     {
     throw new Exception(string.Format("'td' element expected, {0} found.", node.Name));
     }
    
    /// apply the attributes
     ApplyAttributesToControl(node.Attributes, ref tableCell);
    
    /// From this table row element find all the child objects
     /// these might be tables, text or html objects
     if (node.HasChildNodes)
     {
    
    foreach (XmlNode childNode in node.ChildNodes)
     {
     /// this might be an HTML object or another table... or both... (gulp)
     if (childNode.NodeType == XmlNodeType.Element)
     {
     if (childNode.Name.ToLower() == "table")
     {
     tableCell.Controls.Add(GetTableFromNode(childNode));
     }
    
    if (childNode.Name.ToLower() == "fieldset")
     {
     tableCell.Controls.Add(GetPanelFromNode(childNode));
     }
     }
     else if (childNode.NodeType == XmlNodeType.Text)
     {
     tableCell.Text = childNode.InnerText;
     }
     }
     }
    
    return tableCell;
    
    }
    
    /// Process each cell and look for content.
     static Panel GetPanelFromNode(XmlNode node)
     {
     /// create the tableRow
     Panel panel = new Panel();
    
    if (node.Name.ToLower() != "fieldset")
     {
     throw new Exception(string.Format("'fieldset' element expected, {0} found.", node.Name));
     }
    
    /// apply the attributes
     ApplyAttributesToControl(node.Attributes, ref panel);
    
    /// From this panel element find all the child objects
     /// one of which will be a legend tag
     /// then it might be HTML elements, tables or text
     if (node.HasChildNodes)
     {
     /// the legend node will be applied as a panel GroupingText property
    
    foreach (XmlNode childNode in node.ChildNodes)
     {
     /// this might be an HTML object or another table... or both... (gulp)
     if (childNode.Name.ToLower() == "legend")
     {
     panel.GroupingText = childNode.InnerText;
     }
    
    if (childNode.Name.ToLower() == "table")
     {
     panel.Controls.Add(GetTableFromNode(childNode));
     }
     }
     }
     else if (node.InnerText != string.Empty)
     {
     panel.Controls.Add(new Literal() { Text = node.InnerXml});
     }
    
    return panel;
    
    }
    
    ///
    
     /// Add attributes to any web control
     ///
    
    /// Reference to the control to which the attributes are being added
     /// The attributes being added
     static void ApplyAttributesToControl( XmlAttributeCollection attribs, ref T ctrl)
     where T : WebControl
     {
     foreach (XmlAttribute attrib in attribs)
     {
     ctrl.Attributes.Add(attrib.Name, attrib.Value);
     }
     }
    
    }
     }
    
    

    Open the HTML file, and pass the root node to the helper class:

    Table table;
    
    /// check if a template exists for this request form...
     /// TODO: Update this to use the request form name, not the ID.
     string TemplatePath = Server.MapPath(string.Format("~/Private/RequestFormTemplates/{0}.xml", _selectedRequestId));
     if (File.Exists(TemplatePath))
     {
     System.IO.StreamReader myFile = new System.IO.StreamReader(TemplatePath);
     string myString = myFile.ReadToEnd();
     myFile.Close();
     myFile.Dispose();
    
    FileStream fs = new FileStream(TemplatePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
     XmlDocument xmldoc = new XmlDocument();
     xmldoc.Load(fs);
    
    table = TableHelper.CreateASPTableFromXML(xmldoc.DocumentElement);
    
    }
    

    The helper class needs to be fleshed out some more, to provide support for other elements and valid HTML, but it’s almost there.

    If there is an easier way, let me know!