10 rock-solid UI tips

Give your Web applications that rich-client look and feel using ASP.NET. Lots of code samples in this one.

Give your Web applications that rich-client look and feel.

A key component of any software development project is the user interface. Without a robust and well designed interface, you can end up discarding even the best-architected application. The graphical user interface (GUI) is the first, and usually only, face users see when working with your application. It forms the basis of their opinions and gives the critical first impression to users.

One of the challenges of Web development is developing a rich-client look and feel using the basic tools of HTML and JavaScript. Robust and interactive interfaces are possible with this technology but only with a great deal of effort and time. ASP.NET helps clear these hurdles. With ASP.NET, you can present rich-client features to the user quickly and easily using a thin-client delivery method. In this article, I'll give you 10 tips for creating and delivering these types of solutions.

  1. Use SmartNavigation

    The first way to make your browser-based applications feel more like traditional Windows Forms applications is to use the SmartNavigation property from the ASP.NET page document. Smart navigation helps solve the problem of screen flicker when a page requires a postback to complete an operation. When enabling smart navigation, ASP.NET persists the scroll position and focus between postbacks.

    Setting this property also means keeping only the latest page state in the user's browser history. Smart navigation works only with up-level browsers and won't cause any rendering issues for down-level users. Enabling smart navigation is easy: Simply set the SmartNavigation property to True in the page's @page directive:

    <%@ Page Language="vb" AutoEventWireup="false"
       Codebehind="ExpenseSummary.aspx.vb"
       Inherits="Expense.ExpenseSummary" SmartNavigation="True"
    %>

  2. Use independent component publisher controls

    Another option to consider when developing ASP.NET-based interfaces is to use independent component publisher (ICP) controls. The advanced controls Visual Studio .NET provides allow you to create quality applications, but ICP controls give you the ability to create superior applications because they contain additional valuable features. ICP controls also fill holes the default controls don't address.

    Many developers are familiar with using controls in the Visual Basic development environment, but few developers realize the potential of using controls in ASP.NET. These controls work by sending standard HTML and JavaScript to the browser. These controls can render on any browser using standard Internet technologies without any of the issues and security concerns surrounding ActiveX controls (see Figure 1).

    Figure 1
    Figure 1. Through the use of ASP.NET and ICP controls, you can bring rich-client features and functionality to browser-based applications. Here, full-featured graphs are rendered on the server side, but they can interact with the data table in real time via client-side JavaScript.

    Be careful when you use of either the included controls or ICP controls on your Web pages. As a general rule, if you don't need the advanced features or the ability to interact with a control on the server side, you should use the HTML equivalent. Often, developers overuse controls on a page, such as using an ASP Label control for static text display. In such a case, you could use either a Div tag or Span tag more effectively. Every control placed on a page increases the overall server processing that must occur and puts a greater load on the Web server, thus increasing the response time for the page. The amount of increase in page-load time and the maximum number of requests per second your Web server can handle both depend on the amount of ASP controls you can replace with an HTML equivalent.

  3. Add common functionality with user controls

    One of ASP.NET's new features is the user control, which helps simplify the development of great user interfaces. User controls allow you to build your own custom controls from any other control. You can apply this to every page in your application rapidly by packaging common functionality into a reusable package. A great example of this is a menu that will be rendered on a high percentage of your Web pages. By encapsulating this functionality into a user control, you can drag and drop the control onto a Web page and move on.

    Because the user control contains all the presentation and code, maintenance is reduced -- when you update the user control, all your pages will be ready to go.

    For instance, the following user control adjusts the hyperlink based on the value set on the control's SourceURL property. You could position this user control on every page to provide consistent navigation to resources such as help files:

    <%@ Control Language="vb" AutoEventWireup="false"
       Codebehind="UserControlDemo.ascx.vb"
       Inherits="UserControlDemo.UserControlDemo"
       %>
    <LINK href="Styles.css" type="text/css" rel="stylesheet">
    &tl;TABLE id="Table1" cellSpacing="0" cellPadding="0"
      border="0">
       <TR>
          <TD align="right">
             <IMG src="Images/HelpIcon.gif"></TD>
          <TD>
             <asp:HyperLink id="HelpHyperlink" runat="server">
                User Control Demo
             </asp:HyperLink>
          </TD>
       </TR>
    </TABLE>

    Here, the SourceURL property takes in the URL the hyperlink should reference. Then, the absolute path to the file is computed and appended to the page name:

    Public WriteOnly Property SourceUrl() As String
       Set(ByVal Value As String)
          HelpHyperlink.NavigateUrl = "http://" & _
          Context.Request.Url.Host & _
          Context.Request.ApplicationPath & "/" & Value
       End Set
    End Property
    

  4. Create dynamic graphics with System.Drawing

    HTML has limited abilities, and one common challenge in Web development is creating complex graphics dynamically. You can overcome this hurdle by using the extensive capabilities supplied in the .NET Framework's System.Drawing namespace. With System.Drawing, you can perform rich server-side image generation and manipulation such as overlaying, resizing, and cropping images. If the content type is set correctly, you can output the results to any standard I/O stream (see Figure 2). Because both the browser and server treat the results as an image, you can use the ASP.NET output cache to increase your application's responsiveness.

    Figure 2
    Figure 2. This image was rendered dynamically on the server using the functionality exposed by the System.Drawing namespace. Images such as basic drawings can be generated on the fly and streamed to the browser via the standard I/O stream.

    The next code snippet is the page declaration for the SystemDraw Web page. You must switch the content type to image/gif because you're only sending the browser a dynamically created gif file:

    <%@ Page ContentType="image/gif" Language="vb"
        AutoEventWireup="false" Codebehind="SystemDraw.aspx.vb"
        Inherits="Demo.SystemDraw"
    %>

    The PageLoad event will be used to make the appropriate calls to the GDI+ libraries (see Figure 3).

    
    Private Sub Page_Load(ByVal sender As System.Object, _
               ByVal e As System.EventArgs) Handles MyBase.Load
    
         Dim Months As New ArrayList()
         Dim Profits As New ArrayList()
    
         Months.Add("January")
         Months.Add("February")
         Months.Add("March")
         Months.Add("April")
         Months.Add("May")
    
         Dim Random As New Random()
    
         Profits.Add(Random.Next(100000, 999999))
         Profits.Add(Random.Next(100000, 999999))
         Profits.Add(Random.Next(100000, 999999))
         Profits.Add(Random.Next(100000, 999999))
            Profits.Add(Random.Next(100000, 999999))
    
           DrawDemoGraph("Example of System.Drawing", Months, _
                Profits, Response.OutputStream)
     End Sub
    
     Sub DrawDemoGraph(ByVal Title As String, _
               ByVal aX As ArrayList, ByVal aY As ArrayList, _
    ByVal Target As Stream)
    
          Const ColWidth As Integer = 60, _
               ColSpace As Integer = 25, _
               MaxHeight As Integer = 400, _
               HeightSpace As Integer = 25, _
               XLegendSpace As Integer = 30, _
               TitleSpace As Integer = 50
       
         Dim MaxWidth As Integer = (ColWidth + ColSpace) _
                 * aX.Count + ColSpace, _
               MaxColHeight As Integer = 0, _
               TotalHeight As Integer = _
                 MaxHeight + XLegendSpace + TitleSpace
    
          Dim objBitmap As Bitmap = _
               New Bitmap(MaxWidth, TotalHeight)
            Dim objGraphics As Graphics = _
               Graphics.FromImage(objBitmap)
    
         objGraphics.FillRectangle(New SolidBrush(Color.White),_
                0, 0, MaxWidth, TotalHeight)
         objGraphics.FillRectangle(New SolidBrush(Color.Ivory),_
                0, 0, MaxWidth, MaxHeight)
    
         ' find the maximum value
         Dim iValue As Integer
         For Each iValue In aY
             If iValue > MaxColHeight Then _
               MaxColHeight = iValue
         Next
    
         Dim BarX As Integer = ColSpace, _
               CurrentHeight As Integer
         Dim objBrush As SolidBrush = New SolidBrush(Color.Navy)
         Dim fontLegend As Font = New Font("Arial", 11), _
               fontValues As Font = New Font("Arial", 8), _
    fontTitle As Font = New Font("Arial", 24)
    
         ' loop through and draw each bar
         Dim iLoop As Integer
         For iLoop = 0 To aX.Count - 1
             CurrentHeight = (Convert.ToDouble(aY(iLoop)) _
               / Convert.ToDouble(MaxColHeight)) * _
                Convert.ToDouble(MaxHeight - HeightSpace)
    
             objGraphics.FillRectangle(objBrush, BarX, _
               MaxHeight - CurrentHeight, ColWidth, _
               CurrentHeight)
             objGraphics.DrawString(aX(iLoop), fontLegend, _
               objBrush, BarX, MaxHeight)
             objGraphics.DrawString(String.Format("{0:#,###}", _
               aY(iLoop)), fontValues, objBrush, BarX, _
                MaxHeight - CurrentHeight - 15)
    
             BarX += (ColSpace + ColWidth)
         Next
    
         objGraphics.DrawString(Title, fontTitle, objBrush, 0,_
                MaxHeight + XLegendSpace)
    
         objBitmap.Save(Target, ImageFormat.Gif)
         objGraphics.Dispose()
         objBitmap.Dispose()
    End Sub
    
    Figure 3. The DrawDemoGraph function does the work of generating the bars for your graph and rendering the image with the basic functionality of the System.Drawing namespace provided by the .NET Framework. You can leverage the built-in libraries to create your own complex custom images.

  5. Make apps responsive: Stay on the client side

    Creating a great-looking interface is only part of the battle. Even the best-looking applications will fail to gain acceptance if the interface is slow and unresponsive.

    One approach to increasing the responsiveness of your application is performing as much as possible with client-side JavaScript instead of using server-side events. Client-side JavaScript's ability to work without the need for a round trip back to the server gives the user an instant response and removes overhead from both the network and Web servers. Some controls, such as the required field validator, generate JavaScript for up-level browsers automatically. Other controls, such as the custom validator, require you to write some JavaScript to perform their functionality on the client side (see Figure 4). Many advanced ICP controls expose a large section of their API in both server-side and client-side events.

    
    function MyFunction(this_ref, Row, Column, Value, RowLabel,_
               ColumnLabel)
    {
       //Row         - [int]     Row of data item
       //Column      - [int]     Column of data item
       //Value       - [double] Data Value of the item
       //RowLabel    - [string] Row Label of data item clicked
       //ColumnLabel - [string] Column Label of data item clicked
    
       if ((Row<12) && (Column <7)) {
          newColumn = Column - 1;
          eval("document.ROW"+lasti+".src=BLN.src;");
          eval("document.COL"+lastk+".src=BLN.src;");
          eval("document.ULN"+lasti+"_"+lastj+".src=BULN.src;");
      
          eval("document.ROW"+Row+".src=ROW.src;");
          eval("document.COL"+newColumn+".src=COL.src;");
    
          eval("document.ULN"+Row+"_"+Column+".src=ULN.src;");
    
          lasti=Row;
          lastj=Column;
          lastk=newColumn;
       }
      
    }
    Figure 4. This client-side function responds to the client-side events for the mouse-over event of the chart on the page. The function is passed the data points for the currently selected row and column. Then, the function changes the image on the corresponding row and column in the table on the page to show the arrow images. The function also changes the image in the correct cell to underline the data.

  6. Expand the DataGrid

    DataGrid is one of the most commonly used and most powerful inbox controls. Because of DataGrid's complex nature, it's an excellent candidate to extend with client-side JavaScript. One of the most common requests is to make DataGrid display a confirmation box on a record delete. Though the JavaScript to perform this operation is simple, the challenge is to wire the OnClick event of a ButtonColumn to the JavaScript.

    To connect to the event, you should use DataGrid's ItemCreated event. This event fires as each row is rendered in the grid. During the processing of this event, you will acquire a reference to the command button in the row and associate the Dynamic HTML OnClick event to the custom client-side JavaScript:

    public void OnItemCreated(Object sender,
       DataGridItemEventArgs e)
    {
       if (e.Item.ItemType == ListItemType.Item ||
       e.Item.ItemType == ListItemType.AlternatingItem)
       {
          WebControl button =
           (WebControl) e.Item.Cells[0].Controls[0];
          button.Attributes.Add ("onclick",
          "return confirm ("Delete this row?");");
       }
    }
    
    public void OnItemCommand (Object sender,
       DataGridCommandEventArgs e)
    {
       if (e.CommandName == "Delete")
       {
          Results.Text = "You deleted ItemID "" +
            e.Item.Cells[1].Text + """;
       }
    }

    Localize that app

    As more applications are designed as Web sites and are called on to extend across language and cultural boundaries, developers are faced with the challenge of making applications global in scope. Many classes in the .NET Framework -- and, by extension, in ASP.NET -- are culture-aware. To access this awareness, you need to create a CultureInfo object to represent the current culture and associate it with the running thread's CurrentCulture property. Once this link has been made, common functions such as String.Format and DateTime's ToShortDateString will adjust according to the culture formatting.

    ASP.NET must look at the Accept-Language header of the HTTP request to determine a user's culture request. ASP.NET will expose this header as a string array under the UserLanguages property of the Request object.

    Adding some code to the Global.asax file's Application_BeginRequest event will allow you to create the CultureInfo object for the correct language (see Figure 5). Once you have created the CultureInfo object for the user's primary language, it will need to be associated to the current thread processing the request.

    Getting ASP.NET formatting data according to each user's culture preference might be just the first step in making your application span the global community. But in many cases, it might be the only step needed.

    
    Imports System.Threading
    Imports System.Globalization
     
    Public Class Global
      Inherits System.Web.HttpApplication
    
      Sub Application_BeginRequest(ByVal sender As Object, _
       ByVal e As EventArgs)
         Try
            If Request.UserLanguages.Length > 0 Then
             Thread.CurrentThread.CurrentCulture =
    CultureInfo.CreateSpecificCulture_
    (Request.UserLanguages(0))
      
                End If
            Catch
                'Don't error if CreateSpecificCulture fails
            End Try
        End Sub
    End Class
    Figure 5. Adding just a few basic lines of code to the Global.asax file will allow the .NET Framework to format your text to a user's regional settings automatically.

  7. Add row numbers

    Users often ask for the look and feel of Microsoft Excel in an application's data grid. One feature requested often is a set of row numbers on the grid. Using DataGrid's OnItemCreate event allows a sequential set of numbers to be added to the grid as each row is rendered.

    Add a template column as the first column of the grid. As each row is rendered, the code behind the OnItemCreate event will update the text of this column based on the row index from the dataset. Each row of the dataset has an index property that can be used to compute the row number:

    public void OnItemCreated(Object sender,
       DataGridItemEventArgs e)
    {
      if(e.Item.ItemType ==ListItemType.Item ||
         e.Item.ItemType ==ListItemType.AlternatingItem)
       e.Item.Cells[0].Text=(e.Item.DataSetIndex+1).ToString();
    }

    Cause button click on Enter

    Pressing the Enter key on an ASP.NET form doesn't necessarily cause a form submission. To force a particular button on the page to fire and cause a postback on Enter, simply add one line of code:

    Page.RegisterHiddenField("__EVENTTARGET", "BUTTON_NAME")

    Change BUTTON_NAME to the ID of the button to which you want to default. This trick is limited by certain controls in HTML, such as a multiline textbox in which the default action is to create a new line.

    Create wizard interfaces with Panels

    The wizard-based interface has become a mainstay in client-server applications. Although ASP.NET can support this interface with multiple pages, the amount and complexity of the code to persist the data can become unwieldy as the user pages back and forth in the wizard. Using ASP.NET's Panel control can simplify this interface greatly.

    Create a Panel for each step in the wizard and one set of Cancel, Next, Back, and Save buttons on the form. Inside the Panel control are the user-interface elements needed to support the particular step of the wizard.

    In the event handler for each of the command buttons is the code to hide and unhide the correct panels and buttons based on the state of the wizard. At the end of the wizard, the event handler for the Save button will commit the data. Because all the steps of the wizard are in panels on the one page, the event handler has access to all the data of the various steps. The user will believe each step is generating a new page based on the state of the wizard, but the developer has an easier time and less code.

The bottom line

As you can see, building a rich and responsive user interface is easy and beneficial with ASP.NET. By using a combination of basic and advanced tips, you can deliver client-server functionality with the deployment benefits of browser-based applications. Whether you are writing applications for Internet or intranet use, ASP.NET provides a robust platform for writing incredible and responsive interfaces.

Brad McCabe is the technical evangelist for Infragistics. Brad also has been a systems architect and consultant for Verizon Communications and many other clients, and he was a leading .NET evangelist within Ajilon Consulting. His primary interests include ASP.NET, Windows CE .NET, .NET Compact Framework, and Microsoft's networking technologies. E-mail him at brad@infragistics.com.


This article was provided by asp.netPRO Magazine, an online information resource for the ASP.NET developer community, which helps professional software developers build, deploy and run the next generation of dynamic, distributed Web applications quickly and easily. Click here for subscription information.
This was first published in August 2003

0 comments

Oldest 

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

-ADS BY GOOGLE

SearchCloudComputing

SearchSoftwareQuality

SearchSOA

TheServerSide

SearchCloudApplications

Close