Optimizing Responsive Design Using RESS with ASP.Net Web Forms

Due in part to the popularity of open source solutions like Bootstrap and Foundation, responsive web design is standard practice in modern web applications. Responsive design is an attractive solution because of its ability to produce multi-device layouts with relative ease. While responsive design is an excellent solution, it does have its shortcomings and even a well–intentioned design can quickly cause application bloat.

Let’s get an understanding of what the application bloat issue with responsive design is and what we can do to resolve it. We’ll look at solutions using ASP.NET Web Forms and create a responsive layout using the new RadPageLayout component. Later we’ll learn about the Telerik Device Detection framework and how it can bring our responsive application to the next level by giving us server-side control of our responsive behaviors.

Responsive design and app bloat

Some techniques used to create attractive responsive behaviors such as toggling between mobile and desktop friendly UI elements can increase file size and result in slow performance. Responsive design frameworks include a mechanism allowing UI components to be displayed or hidden depending on the device size, for example in Bootstrap’s responsive utilities contain the visible-[size] and hidden-[size] CSS classes. These utility classes work as advertised, however they only hide the content visually, the markup, images, and data required to produce the UI is still sent to the browser. This is a wasteful process that can result in a bloated page size. This is why Bootstrap warns users to use this technique sparingly in it’s documentation:

Try to use these on a limited basis and avoid creating entirely different versions of the same site.

Utility classes are good for prototyping scenarios and for minor UI adjustments but aren’t robust enough to use for difficult tasks.

For example, if we use Bootstrap’s visible utility classes, we can replace a large banner image with a smaller one depending on the device’s screen size. In the code below the large header image is only shown on medium and large screens, while the small header image is shown on small and extra small screens. On the surface the desired effect works flawlessly, however if we take a deeper look, we can see that the browser is still downloading the resources that are not “visible” to the user.

<img src="Content/large-header-image.png" class="visible-lg-inline visible-md-inline" />
<img src="Content/small-header-image.png" class="visible-xs-inline visible-sm-inline" />
Example 1 large screen

Example 1 large screen

 

Example 1 small screen

Example 1 small

 

Example 1 inspection

Example 1 inspection

Using Chrome developer tools we can reveal what resources are being received on the client. As you can see in the screen shot, both images are being downloaded. By downloading both images, clients must download a total of 653kb of image data regardless of their screen size. This is a complete waste of bandwidth and a cause for slow mobile performance.

In this example we used images, but hiding any type of content creates the same scenario.

So if hiding content is bad practice, what can be done to effectively optimize content for a specific screen size? One solution is to use server side functionality to only serve what the client needs.

RESS: Responsive Design + Server Side Components

We saw that CSS hide/show techniques when used with responsive design can cause page bloat. This is because the UI changes are made solely on the client side. This means that the client must receive the markup, images, and other data before it can respond to the device’s screen size. A more proactive approach can be taken by including the server in the process. By using server side code, the application can deliver just the necessary content for a specific device’s needs. Unlike the visible and hidden CSS classes, RESS techniques can depend on the server to perform these actions thus reducing page size.

To implement RESS, we’ll use one of the most “server friendly” web development platforms there is, ASP.NET Web Forms. Web forms is a well established platform in the enterprise world, and many enterprise applications can benefit from new mobile techniques like responsive design and RESS. In addition we’ll also be using some Telerik technologies, giving us two powerful solutions to get the job done: RadPageLayout and the Device Detection Framework.

Creating Layouts on The Server Side

With Telerik’s RadPageLayout, responsive design can developed completely with server side components. RadPageLayout is a grid based framework similar to Bootstrap or Foundation. All of the responsive grid functionality is included: a twelve column nest-able grid, multiple device size options, column width offsets, and source order functionality. However, the RadPageLayout sets it self apart from the other frameworks by being fully configurable as a server side component. ASP.NET Web Forms developers will find familiarity with the RadPageLayout, as the control’s property panel reduces the learning curve associated with responsive design. All row, column and screen size attributes can be set via the properties window in Visual Studio.

Adding a RadPageLayout is as simple as dragging the component from the toolbox, or creating the markup below by hand. Notice the GridType is set to “Fluid”, this will tell our RadPageLayout that we want our layout to expand or contract to fit the clients screen size.

<telerik:RadPageLayout ID="RadPageLayout1" runat="server" GridType="Fluid">
</telerik:RadPageLayout>

Now we can add multiple rows and columns to our layout, starting with rows first then breaking each row in to columns that span a twelve unit grid. In the example below, if we wanted a row with two 50% width columns, we would create a layout with one row, and that row would have two columns which span six units each (totaling 12 units). We can even define how many units to span depending on what screen size they are viewed at using the Span[size] properties.

<telerik:RadPageLayout ID="RadPageLayout1" runat="server" GridType="Fluid">
    <Rows>
        <telerik:LayoutRow ID="LayoutRow1" runat="server" AccessKey="" HtmlTag="Div" RowType="Row" WrapperHtmlTag="None">
            <Columns>
                <telerik:LayoutColumn ID="LayoutColumn1" runat="server" HtmlTag="Div" Span="6" SpanMd="12" SpanSm="12" SpanXs="12">
                <!-- First Column -->
                </telerik:LayoutColumn>
                <telerik:LayoutColumn ID="LayoutColumn2" runat="server" HtmlTag="Div" Span="6"  SpanMd="12" SpanSm="12" SpanXs="12">
                <!-- Second Column -->
                </telerik:LayoutColumn>
            </Columns>
        </telerik:LayoutRow>
    </Rows>
</telerik:RadPageLayout>

Any of the RadPageLayout and its components, LayoutRow and LayoutColumn, can be set to runat="server" for server side access to the control at run-time.

Developers who are more comfortable with using the property window can even create rows and columns there as well.

The RadPageLayout is the first piece of RESS. The RadPageLayout itself is only a responsive grid system, and while its properties are capable of being changed server side at run-time, it is still relies on CSS and the client. To get full control over what is sent to the client we will need an additional tool, the Telerik Device Detection Framework.

Device Detection Implementation

In order to perform RESS functionality with Web Forms, we need to be able to identify device sizes from the server side. This would normally involve creating and managing a collection of User Agent strings which identify a web browser and the type of device requesting content from the server. Thankfully device detection can easily be added to any application by using the Telerik Device Detection Framework.

The Device Detection Framework is an easy to use API that detects whether the screen is small, medium, large or extra large. Simply reference the Device Detection Framework and pass the UserAgent string from the Request and the Detector will bring back the corresponding client screen size.

DeviceScreenSize screenSize = Detector.GetScreenSize(Request.UserAgent);
if (screenSize == DeviceScreenSize.ExtraLarge)
{
    //ExtraLarge screen specific code here
}

Continuing with the examples used earlier, let’s see how all of these techniques come together on the client and server to make a complete experience.

First, let’s revisit the image size problem. We will be showing a large banner graphic only to medium and large screens, while showing a smaller graphic on less capable devices. We’ll let the RadPageLayout handle responsive content, except this time we will control the images sent to the client by using the Device Detection Framework. By showing and hiding the images on the server side, we will no longer be sending extra data to the client. We’ll also use some CSS to ensure that any image that is rendered is fluid with the layout by setting its max-width to 100%.

<style>
    .img-responsive {max-width: 100%;}
</style>
<asp:Image ID="LargeBannerImage" ImageUrl="~/Images/large-header-image.png" CssClass="img-responsive" runat="server" />
<asp:Image ID="SmallBannerImage" ImageUrl="~/Images/small-header-image.png" CssClass="img-responsive" runat="server" />

<telerik:RadPageLayout ID="RadPageLayout1" runat="server" GridType="Fluid">
    <Rows>
        <telerik:LayoutRow ID="LayoutRow1" AccessKey="" HtmlTag="Div" RowType="Row" WrapperHtmlTag="None">
            <Columns>
                <telerik:LayoutColumn ID="LayoutColumn1" HtmlTag="Div" Span="6" SpanMd="12" SpanSm="12" SpanXs="12">
                    <!-- First Column -->
                    <h2>Title</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ac faucibus lacus. Nam tincidunt dolor nec lacus pharetra scelerisque. Vestibulum eget maximus erat. Etiam euismod porta felis a placerat. Sed risus dolor, pretium sed metus et, maximus aliquet lacus. Praesent laoreet vulputate nisl eu scelerisque.</p>
                </telerik:LayoutColumn>
                <telerik:LayoutColumn ID="LayoutColumn2" HtmlTag="Div" Span="6" SpanMd="12" SpanSm="12" SpanXs="12">
                    <!-- Second Column -->
                    <h2>Title</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ac faucibus lacus. Nam tincidunt dolor nec lacus pharetra scelerisque. Vestibulum eget maximus erat. Etiam euismod porta felis a placerat. Sed risus dolor, pretium sed metus et, maximus aliquet lacus. Praesent laoreet vulputate nisl eu scelerisque.</p>
                </telerik:LayoutColumn>
            </Columns>
        </telerik:LayoutRow>
    </Rows>
</telerik:RadPageLayout>

.aspx

protected void Page_Load(object sender, EventArgs e)
{
    var screenSize = Detector.GetScreenSize(Request.UserAgent);

    var isMediumOrSmaller = screenSize == DeviceScreenSize.Medium || screenSize == DeviceScreenSize.Small;
    var isLargeOrLarger = screenSize == DeviceScreenSize.Large || screenSize == DeviceScreenSize.ExtraLarge;

    LargeBannerImage.Visible = isLargeOrLarger;
    SmallBannerImage.Visible = isMediumOrSmaller;
}

.aspx.cs

In the code behind, we are using device detection to control the visibility of our banner images. The LargeBannerImage is set visible when a Large or ExtraLarge screen is detected, while the SmallBannerImage is set visible when a Medium or Small screen is detected.

The results can be tested by using device emulation in your web browser tools, just as we did in the first example.

Example 2 small screen

Example 2 small screen

Example 2 inspection

Example 2 inspection

In the developer tools, notice that the client is only receiving the small header image at just 5KB.

This practice can be applied to more than just images. Single controls, or container controls like the RadPageLayout, LayoutRow, and LayoutColumn can be modified server side to optimize the user experience. But remember, “with great power comes great responsibility” make sure you use the technique wisely, hiding content while not giving a user a way to access it can be a problem too. In order to make sure the user can always get what they need, we can deploy another technique called lazy loading.

Lazy Loading

The features of ASP.NET Web Forms give us unlimited options for building UIs based on the needs of the user and their device. Because of ASP.NET’s ascx user controls, pages can be composed at run time. Pages can even have sections lazy loaded via ajax a la-carte allowing the user to load content on demand as they see fit. Lazy loading content has the advantage of keeping content hidden, but still available to the user if they want to see it.

For this example we’ll change things up a bit. This time we’ll use a dashboard like application that has a grid of data, and a pie chart. In this scenario, we want to modify the data grid so that it only shows pertinent data. In addition, we don’t always need to see the pie chart, so we will be hiding it by default on mobile. Since we don’t want our pie chart to be completely in accessible on mobile, we will give the user the option to lazy load the pie chart via a button using AJAX. By allowing the user to lazy load the content, we are putting them in control of their user experience.

To begin, a RadGrid and RadHtmlChart are added to a RadPageLayout. In the LayoutColumn containing the chart, a Panel containing a RadButton is created. The button will only be available when a small device is detected and the chart is not visible.

<telerik:RadPageLayout ID="RadPageLayout1" runat="server" GridType="Fluid">
        <Rows>
            <telerik:LayoutRow ID="LayoutRow1" runat="server" AccessKey="" HtmlTag="Div" RowType="Row" WrapperHtmlTag="None">
                <Columns>
                    <telerik:LayoutColumn ID="LayoutColumn1" runat="server" HtmlTag="Div" Span="6" SpanMd="12" SpanSm="12" SpanXs="12" ToolTip="">
                        <!-- First Column -->
                        <telerik:RadGrid ID="RadGrid1" runat="server" GroupPanelPosition="Top" RenderMode="Auto" SelectMethod="BindProducts" Skin="Metro" CellSpacing="-1" GridLines="Both" AllowPaging="true" PageSize="15">
                                  <!-- Grid code... -->
                        </telerik:RadGrid>
                    </telerik:LayoutColumn>
                    <telerik:LayoutColumn ID="LayoutColumn2" runat="server" HtmlTag="Div" Span="6" SpanMd="12" SpanSm="12" SpanXs="12">
                        <!-- Last Column -->
                        <asp:Panel ID="AnalysisPanel" runat="server" Visible="false" CssClass="analysis-panel-action">
                            <telerik:RadButton ID="ShowAnalysisButton" runat="server" CssClass="rbPrimaryButton" OnClick="ShowAnalysisButton_Click" ButtonType="SkinnedButton" Text="Show Inventory Analysis"></telerik:RadButton>
                        </asp:Panel>
                        <telerik:RadHtmlChart ID="RadHtmlChart1" runat="server" RenderMode="Auto" SelectMethod="GetInventoryAnalysis" ChartTitle-Text="Inventory Analysis" Width="100%">
                           <!-- Chart code... -->
                        </telerik:RadHtmlChart>
                    </telerik:LayoutColumn>
                </Columns>
            </telerik:LayoutRow>
        </Rows>
    </telerik:RadPageLayout>

Next we’ll need a RadAjaxManager to wire up our lazy loading mechanics. We need to use AJAX here, without it the entire page would need to post back, defeating the purpose of saving bandwidth.

<telerik:RadAjaxManager ID="RadAjaxManager1" runat="server" DefaultLoadingPanelID="RadAjaxLoadingPanel1">
    <AjaxSettings>
        <telerik:AjaxSetting AjaxControlID="RadGrid1">
            <UpdatedControls>
                <telerik:AjaxUpdatedControl ControlID="RadGrid1" UpdatePanelCssClass="" />
            </UpdatedControls>
        </telerik:AjaxSetting>
        <telerik:AjaxSetting AjaxControlID="ShowAnalysisButton">
            <UpdatedControls>
                <telerik:AjaxUpdatedControl ControlID="AnalysisPanel" UpdatePanelCssClass="" />
                <telerik:AjaxUpdatedControl ControlID="RadHtmlChart1" UpdatePanelCssClass="" />
            </UpdatedControls>
        </telerik:AjaxSetting>
    </AjaxSettings>
</telerik:RadAjaxManager>

Finally, we wire up the server side. This time we’ll take further advantage of the Device Detection Framework by setting some additional properties on the grid. Since we have limited space on a small device, we’ll choose to not display some of the data grid columns, the user can choose to show them if they are needed. We can further minimize the data grid by decreasing the page size from 15 to 5. The chart’s visibility will be set to false making sure its HTML is not sent to the client, and in its place we’ll render the panel which contains our “Show Inventory Analysis” button.

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        var screenSize = Detector.GetScreenSize(Request.UserAgent);
        if (screenSize == DeviceScreenSize.Small)
        {
            RadGrid1.Columns.FindByDataField("ProductID").Display = false;
            RadGrid1.Columns.FindByDataField("Discontinued").Display = false;
            RadGrid1.Columns.FindByDataField("UnitsInStock").Display = false;
            RadGrid1.PageSize = 5;
            RadHtmlChart1.Visible = false;
            AnalysisPanel.Visible = true;
        }
    }
}

protected void ShowAnalysisButton_Click(object sender, EventArgs e)
{
    RadHtmlChart1.Visible = true;
    AnalysisPanel.Visible = false;
}

Below are the final results. The user is presented with a full display on a large screen. Mobile users are given access to the exact same data, except in a more optimized experience.

Example 3 large screen

Example 3 large screen

Example 3 small screen

Example 3 small screen

Example 3 small screen w/ AJAX

Example 3 small screen w/ AJAX

Small, after AJAX request

The Wrap Up

Responsive Web Design is here to stay and it’s important to realize its strengths and weaknesses. While it is a great solution to many problems, it comes with its own nuances. Using only HTML, CSS, and JavaScript is a good start, especially if you need to prototype application behavior, but remember to take a close look and see what is happening on the client’s browser. There is a lot of room to optimize, gain performance, and provide a better user experience on mobile. Responsive Design and Server Side components can give your app the edge it needs to set itself apart from other applications.

Comments