Monday, October 31, 2005

HtmlControls vs. WebControls

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/htmlcontrols_vs_webcontrols.htm]

Plain Html provides form controls via the tag. These are standard controls like textBoxes, dropdowns, and radiobuttons. ASP.Net provides its own version of these controls. Ultimately the ASP.Net version gets rendered as a html. For development you can use either the WebControls or the normal html controls run as server controls. The table below offers some comparisons:

 WebControlHtmlControl runat=server
DescriptionThe ASP.Net controls residing in System.Web.UI.WebControlsNormal html controls that are run at the server. These reside in System.Web.UI.HtmlControls. Setting a HtmLControl to be runat=server does not automatically make it an ASP.Net webControl.
Pro
  • Easier to initially use. For example, automatically works with validators (a disabled Html control doesn't).
  • Many more webControls than htmlControls (such as the dataGrid, validators, and placeHolders)
  • Lighter - doesn't have all the extra properties of WebControls
  • Easier to hook up to JavaScript.
Con
  • Heavier - has more properties, state, and methods, such as the AutoPostBack. Just compare HtmlInputText and TextBox.
  • Lacks the extra features of WebControls

The Pros and Cons mean that they each have scenarios where one is better than the other. For cases like textBox and dropdown where both can provide the same control: HtmlControls are good for high performance apps because you use only the functionality that you need, WebControls are good for rapid development because it automatically gives you more stuff (which then must be paid for with performance).

Thursday, October 27, 2005

Using a single html file for easy management

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_a_single_html_file_for_easy_management.htm]

Normally you want to split your html objects (like styles, scripts, and data) into separate files to make things more reusable. However sometimes when you're creating an internal developer report (for status, confirmation, quick info, etc...), it's easier to give the user a single html file than a whole collection of files. For example, suppose you're making an internal WinForms tool that assists with some development task, and then generates an Html status report. As a single file, it's easier to mail to others or copy into different directories,.

An html page can have several components, each as separate, physical files:

  • The main html page itself
  • A style sheet
  • External JavaScripts
  • Inline frames
  • Images
  • An Xml data source

We can embed both the style sheet and JavaScript into the head of the main html file itself:

<html>
<
head
>
<
title>Security Rightstitle>


<Style>
.TableHeader
{
    font-weight: bold;
    background-color: #FFFFCC;   
}
Style>


<script language=javascript>

script>


head>
<
body>

We can imitate some features of inline frames via CSS by scrolling inline tables.

If you're already automatically generating the report, then you've already essentially split data from presentation and therefore you can also hardcode the data directly into the Html itself.

That leaves us with Images - which I'm not sure of any way to embed images into an Html file.

However all in all, having the option of putting your styles, scripts, inline frames, and data all into a single file may make it easier to manange for internal reports.

Sunday, October 23, 2005

CodeSmith CodeGeneration: Super XSLT

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/codesmith_codegeneration_super_xslt.htm]

It's very common to have an XML data file, and then based on that to generate something else. Several years ago EXtensible Stylesheet Language Transformation (XSLT) was developed to handle this. The intent is that given an initial file, XSLT lets you program a template to transform that file into something else. For example, you could store just the raw data in an Xml file, and then have an XSLT template transform that into an html file. This new file is usually bigger and more redundant because it's doing something with the data, like adding extra html to format and present it. Another example would be storing base data in an Xml config file, and then transforming it to a lengthy SQL script.

The problem is that XSLT requires a entirely new skillset, isn't as powerful as a full-fledged language (like C#), and lacks the supporting development tools and IDE that make writing and debugging easier. You could use C# and the System.Xml namespace to manually transform the file, but that's tedious.

Another solution is to use a code-generation tool like CodeSmith. CodeSmith can take an Xml document, and given its XSD Schema, create a strongly typed object (which includes intellisense and collection capabilities). You can program a template in the CodeSmith IDE using C#. It's essentially like Super XSLT. By letting you use a language that you already know (either C# or VB), creating the strongly typed object, and using a standard IDE with debugging, using CodeSmith for batch (i.e. compile time) xml transformations can be a real time saver. It's also probably much easier for other developers to read and maintain a C#-based template than an XSLT one.

Tuesday, October 18, 2005

Validation Tips for Validators

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/validation_tips_for_validators.htm]

ASP.Net provides a convenient, pre-built validation system. It consists of individual validation controls (like required fields), custom validators, potentially your own controls derived from BaseValidator, and a summary control to tie it all together

Sometimes we want to force, or bypass, validation on the client. We can do this by calling ASP.Net's own auto-generated functions. Drag a Required Validator onto a WebForm, run it, view the page source, and you'll see some javascript auto-generated by ASP.Net. This includes both a Page_ClientValidate and __doPostBack function.

GoalExplanationHow to do it
Force validation on the client Be able to check as much at the client before posting back. call Page_ClientValidate()
Bypass client validation checksBe able to have certain controls bypass validation checks. For example you may two unrelated sections on a page, and you'll want to click a "calculate" or "search" button on the first section without triggering the unrelated validators on the other section.call postBack: __doPostBack('','')

Also keep in mind as you develop your validation system:

  • Validators (including custom) don't fire if their ControlToValidate fields are empty (except for RequiredValidator itself).
  • Recall that disabled html fields do not persist their value to the server. This may throw off validators on fields that the user can disable at run time.
  • You can debug you client side validators, as they are just JavaScript.

ASP.Net provides custom validators. These are great for fine-tuning your validation. Some tips to keep in mind:

  • You can embed extra attributes in the validator's innerHtml, and then reference them in the client & server functions. This is useful to make validators reusable (control independent).
  • You can omit the controlToValidate property to ensure that validation only fires on postback as opposed to when focus leaves that individual control. This is very useful when validating groups of controls.
  • You can use the methods in WebUIValidation.js to access and compare values.
  • You need both client and server validation functions.

Here's a sample custom validator:

Client Code - Validator Html:

<asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="CustomValidator"
MaxLength="10"
ControlToValidate="File1"
ClientValidationFunction="ValidateMaxLength"
OnServerValidate="ValidateMaxLength">
asp:CustomValidator
>

Here we added the MaxLength attribute. We can reference it in both the client and server validation functions.

Client Code - JavaScript validation function:

    <script language="javascript">

       function ValidateMaxLength(source, arguments)
       {
              var intMaxLength = source.MaxLength;
              if (arguments.Value.length > intMaxLength)
                  arguments.IsValid = false;
              else
                  arguments.IsValid = true;
       }
    script>

Server Code - validation function:

    protected void ValidateMaxLength (object source,    ServerValidateEventArgs value)     {      try       {        int intMaxLength = Convert.ToInt32(        ((System.Web.UI.WebControls.CustomValidator)source        ).Attributes["MaxLength"] );        if (value.Value.Length > intMaxLength)          value.IsValid = false;        else          value.IsValid = true;      }      catch       {        value.IsValid = false;      }    }

Thursday, October 13, 2005

Trigger a page method from a User Control

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/trigger_a_page_method_from_a_user_control.htm]

[UPDATE 11/20/2006] - New post with code sample

User controls let you group UI-functionality together into a resuable control. For example, headers, footers, menus, and groups of controls like userId-passwords and addresses all make good user controls. Effective components need to both pass data in (i.e. be able to have the page set the values of the user control), and pass data out (be able to have the page retrieve the values from fields in the UserControl). Besides just passing data, we also want to be able to trigger events. For example you could click a button on a UserControl or Page, and that triggers a method from the UC. But how do you do it the other way - click a button on the user control and have it trigger a method on the page. There are several ways.

  • One way of using delegates, as mentioned in this article I wrote back in early 2004 for 4GuysFromRolla.

However, I have since seen better ways, and I would suggest either:

The last two links are from MSDN, and therefore have code samples in both VB.Net and C#.

Thursday, October 6, 2005

Rapid Development with the XmlSerializer

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/rapid_development_with_the_xmlserializer.htm]

Sometimes you'll want to save your business objects to an xml file. All the Xml parsing and manipulation can be tedious, but the .Net Framework provides a great class to save us from this: XmlSerializer. This class serializes objects (i.e. their internal state, like property values) to and from Xml. You could add methods to your objects like WriteToXml and ReadFromXml to encapsulate this logic. For example, the following employee class has a few methods, and the Xml Serialization methods:

    public class Employee
    {
        public Employee()
        {
        }

    #region Public Properties

        private string _strName;
        public string Name
        {
            get {return _strName;}
            set {_strName=value;}
        }

        private int _intId;
        public int EmployeeId
        {
            get {return _intId;}
            set {_intId=value;}
        }

    #endregion

    #region Public Methods

        public bool AddOrder()
        {
            return true;
        } //end of method

 
    #endregion

    #region Xml Serialization

    public void WriteToXml(string strPath)
    {
      System.Xml.Serialization.XmlSerializer x = new XmlSerializer(typeof(Employee));
      System.IO.StreamWriter sw = null;

      try
      {
        sw = new StreamWriter(strPath);
        x.Serialize(sw,this);
      }
      finally
      {
        if (sw != null)
          sw.Close();
      }
    }

    public static Employee ReadFromXml(string strPath)
    {
      System.Xml.Serialization.XmlSerializer x = new XmlSerializer(typeof(Employee));
      System.IO.StreamReader sr = null;

      try
      {
        sr = new StreamReader(strPath);
        Employee e = (Employee)x.Deserialize(sr);
        return e;
      }
      finally
      {
        if (sr != null)
          sr.Close();
      }
    }

    #endregion

    } //end of class

Both methods require a physical file path. The Write is an instance method that returns void (it writes the current state of the object). The Read is a static method that returns an instance of the type.

Serializing this to Xml produces the following file. Notice how the values of the properties are automatically handled. While this example is a direct mapping of object properties to xml nodes, XmlSerializer provide attributes for more advanced features.

xml version="1.0" encoding="utf-8"?>
<
Employee
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Name>TimName>
  <EmployeeId>3EmployeeId>

Employee>

We can also verify the read-write integration with the following test. First we create an object, then we save it to Xml, then we read it into another object, and finally compare the properties.

    [Test] public void SaveToXml()
    {
      string strPath = @"C:\temp\emp.xml";

      Employee e= new Employee();
      e.EmployeeId = 3;
      e.Name = "Tim";

      //Save
      e.WriteToXml(strPath);

      //Get
      Employee e2 = Employee.ReadFromXml(strPath);

      Assert.AreEqual(e.EmployeeId,e2.EmployeeId);
      Assert.AreEqual(e.Name,e2.Name);

    }

XmlSerializer makes this easy, and is a useful concept to have.

Tuesday, October 4, 2005

CSS Tip: Scrolling inline tables

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/css_tip_scrolling_inline_tables.htm]

A common UI problem is displaying a large amount of information in only a little space. Different user requirements allow for different solutions. For example, perhaps the user has 50 items, but only needs to see 10 at a time. The entire list is still small enough that you can bring all the data to the client - don't need to page through it with a previous/next button doing server hits. Technically, this could be implemented by putting all 50 items in a sub-page, and then displaying that sub-page in the main page as an inline frame. This would fulfill the user requirements:

  • You could set the frame size to be only 10 items.
  • All the data is still at the client - so it's quick
  • The user can conveniently scroll through the list.

While this functionally works, it's technically tedious. Having a separate sub-page means extra JavaScript to pass values between the main and sub page (such as when you save and you want to see the item selected). It also means one more page to keep track of. An alternative solution is to use CSS to inset the table within a div region:

  <body MS_POSITIONING="FlowLayout">
    <form id="Form1" method="post" runat="server">
      <P>This is an inline table test:P>
      <P>
      P>
      <table width="100%">
        <tbody>
        <tr>
          <td width="50%">
                <div style="BORDER-RIGHT: 1px inset; BORDER-TOP: 1px inset; Z-INDEX: 1; OVERFLOW: auto; BORDER-LEFT: 1px inset; BORDER-BOTTOM: 1px inset; HEIGHT: 100px">
        <TABLE id="Table1" cellSpacing="1" cellPadding="1" width="100%" border="1">
          <tbody>
          <TR>
            <TD>aaaaaTD>
            <TD>bbbTD>
          TR>
          <TR>
            <TD>bbbbbTD>
            <TD>bbbTD>
          TR>
          <TR>
            <TD>cccccTD>
            <TD>bbbTD>
          TR>
          <TR>
            <TD>dddddTD>
            <TD>bbbTD>
          TR>
          <TR>
            <TD>eeeeeeeeeeTD>
            <TD>bbbTD>
          TR>
          <TR>
            <TD>fffffffffTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>gggggggggTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>hhhhhhTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>iiiiiiiiTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>jjjjjjjTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>kkkkkkkkkTD>
            <TD>bbbTD>
          TR>
                    <TR>
            <TD>llllllllllllTD>
            <TD>bbbTD>
          TR>
        tbody>
        TABLE>
      div>
          td>
          <td>Other stufftd>
        tr>
      tbody>
      table>

      <P>P>
    form>
  body>

This technique is quick to develop, has the same user benefit, and is all around cleaner.

Sunday, October 2, 2005

Anchor onclick vs. Href

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/anchor_onclick_vs_href.htm]

ASPX, including all its "special" features like postbacks and validators, ultimately renders as standard Html. So it's good to understand how certain Html components, like anchors, work.

A simple anchor just has an target page in the Href, look like so:

AnchorLink

Clicking this anchor will always redirect to PageB.aspx. The Href property can also contain a method to call. For example, ASP.Net renders LinkButtons as anchors whose Href calls the postback method:

href="javascript:__doPostBack('LinkButton1','')">LinkButton

Anchors can also have an onclick event. You could have this method call a message box, set hidden fields, or do whatever else you needed. However, the Href will not be called if the onclick returns false. For example, you could create a "Do you want to save?" yes-no MessageBox like so:

AnchorLink

The JavaScript confirm() method displays a yes-no confirm box, and returns either true or false depending on what the user clicked. Therefore if the user clicks no, the messageBox returns false, and the href is never activated. This concept applies whether the Href contains a navigation page or JavaScript. For example, you could add the confirm box to the LinkButton, and block the postback if the user answered "No" to a certain question.

Knowing the relationship between the anchor onclick and href lets you develop a more interactive and rich UI.