DaedTech

Stories about Software

By

ASP Webforms Validation 101

Today I’m going to delve into a topic I don’t know a ton about in the hopes that someone who knows less than me will stumble onto it and find it helpful. As I’ve alluded to here and there, I’ve spent the last couple of months doing ASP Webforms development, which is something I’d never done before. I’ve picked up a handful of tips and tricks along the way. Today I’m going to walk through the basics of validation.

Start out by creating a new ASP.NET Webforms project:

This gives you an incredibly simple Webforms project that we can use for example purposes. If you open up the login form, you’ll find a boilerplate login form laid out in markup. Let’s take a look at the User Name controls:

  • User name
  • What we’ve got here is the most basic form control validator, the required field validator. The “ControlToValidate” attribute tells us that it’s going to validate the “UserName” text box and the “ErrorMessage” attribute contains what will be displayed if the validation fails (i.e. there is nothing in the field when you submit the form):

    Pretty standard stuff. Let’s switch things up a little, though. Let’s add another label after the text box with something goofy like “is logging in.” This is something I bumped into early on as a layout issue and had to poke and google around for. Here’s the new look for the markup:

  • User name is logging in.
  • And here’s what it looks like:

    But that’s no good. We want this on the same line as the text box itself or it looks even goofier. Well, counterintuitive as it seemed to me, the validator field defaults to taking up the space that it would occupy if it were always visible. You can alter this behavior, however, by adding the attribute Display=”Dynamic” to the validator tag. Once you do this, the new label will appear on the same line–unless you mess up. Then the validator will resume taking up space, bumping the new label to the next line. Okay, okay, I’m no UX guru, but the important thing here is that you can set the space occupation behavior of your validators.

    The next lesson I learned was that I could use validators for comparing values as well as doing typechecks. This tripped me up a bit too because I would have assumed that there was some kind of TypeCheckValidator, but this isn’t the case. Instead, you have to use CompareValidator. Let’s say that we want users to have to log in with a decimal representing a valid currency. (“Why,” you ask? Well, because we’re insane 🙂 .) This is what it would look like:

  • User name is logging in.
  • The new validator shares some commonality with the existing one, but take special note of the “Operator” and “Type” attributes. Both of these fields are necessary. For anyone who has read my various rants about abstractions, would you care to guess why I found this completely unintuitive? Well, I don’t know about you, but I personally don’t tend to think of “DataTypeCheck” as an “Operator” (perhaps an “operation,” but even that seems like a stretch). I would have expected either a DataType validator or else the Compare validator simply to need the type specified, at which time it would do a type check. But, I digress.

    The next sticking point that I encountered was that I had a particular form where I wanted to validate something that wasn’t part of a text box. I thought I was dead in the water and would have to do something sort of kludgy, but CustomValidator was exactly what I needed. Let’s say that I wanted to verify that the weird label I’d created does not, in fact, contain the word “is.” (This isn’t necessarily as silly as it sounds if there’s code that alters this label dynamically based on other inputs.) If I point any validator at this control as the “ControlToValidate”, I’ll get an exception saying that it cannot be validated. But I can omit that property and specify an event handler.

    Add the custom validator to your markup and you get this:

  • User name is logging in.
  • And add the following to your code behind:

    public void PointlessLabelValidator_ServerValidate(object sender, ServerValidateEventArgs e)
    {
        var labelText =((Label)LoginControl.FindControl("PointlessVerb")).Text;
        e.IsValid = !labelText.Contains("is");
    }
    

    Now launch the web app again and type a number for the user name and something for the password and observe the new error message. It’s important to note here that you need to satisfy the other validation constraints because the custom validator operates a little differently. The validators we’ve added up until now work their magic by sending validation java script over the wire to the client and so validation is client-side and immediate. Here, we’re performing a server-side validation. This server-side custom validation will be short-circuited by client-side failures, which is why you need to fix those before seeing the new one.

    And that’s my brief primer on validators. This is neither exhaustive nor the equivalent of a nice book written by a Webforms guru, but hopefully if you’re here it’s helped you figure out a few basics of Webforms validation.

    By

    Adding a Second Header to an ASP.NET Gridview

    Last week, I had some specs to create a grid view in ASP that had normal column headers, as one might expect, and then also “super” column headers, where the columns were categorized. So, if, for instance, you had columns for “Dog”, “Cat”, and “Bird”, those three headers might have a “super-header” called “Pet”, in what basically amounts to a two-tiered categorization hierarchy.

    Visually, what I wanted to do was create a second header row on top of the one populated from my data source, but there didn’t seem to be any good way to make this happen through the properties of the GridView. It also seemed like a mess to try to do this through CSS and somehow matching client side HTML styling with a service-side generated table. So, to combat this, I got ahold of the grid view’s table and manually added a row to it:

    protected void GridView_DataBound(object sender, EventArgs e)
    {
        var myGridView = (GridView)sender;
        if (myGridView.Controls.Count > 0)
            AddSuperHeader(myGridView);
    }
    
    private static void AddSuperHeader(GridView gridView)
    {
        var myTable = (Table)gridView.Controls[0];
        var myNewRow = new GridViewRow(0, -1, DataControlRowType.Header, DataControlRowState.Normal);
        myNewRow.Cells.Add(MakeCell());
        myNewRow.Cells.Add(MakeCell("PETS", 3));
        myNewRow.Cells.Add(MakeCell("ZOO ANIMALS", 3));
        myNewRow.Cells.Add(MakeCell("VARMINTS", 3));
        myNewRow.Cells.Add(MakeCell("GAME ANIMALS", 3));
        myTable.Rows.AddAt(0, myNewRow);
    }
    
    private static TableHeaderCell MakeCell(string text = null, int span = 1)
    {
        return new TableHeaderCell() { ColumnSpan = span, Text = text ?? string.Empty, CssClass = "table-header" };
    }
    
    

    What’s going on here is that when the grid is data bound, I check to see if data exists and, if so, I add my “super header”. This consists of grabbing the grid view’s first control, which is a table. This is kind of hackalicious in that a change to the ordering of the grid view’s controls collection is a breaking change, but c’est la vie. Once we’ve got ahold of that table, it becomes relatively straightforward to create a row for it using code and to assign it a class to allow further styling in CSS.

    So, quick tip for today, and my first ASP.NET oriented one, as I learn the ins and outs of webforms and hopefully later ASP.NET MVC.

    Cheers!

    Edit: A reader named Patrick Hayden found this useful in his travels, but he was working in Visual Basic.  So for anyone interested in doing this in VB, here is his code:

    Protected Sub GridViewBegroting_DataBound(ByVal sender As Object, ByVal e As EventArgs) Handles GridViewBegroting.DataBound
      Dim myGridView As GridView = sender
      If myGridView.Controls.Count > 0 Then
        AddSuperHeader(myGridView)
      End If
    End Sub
    
    Protected Sub AddSuperHeader(ByVal gridView As GridView)
      Dim myTable As Table = gridView.Controls(0)
      Dim myNewRow As GridViewRow = New GridViewRow(0, -1, DataControlRowType.Header, DataControlRowState.Normal)
      myNewRow.Cells.Add(MakeCell())
      myNewRow.Cells.Add(MakeCell("PETS", 3))
      myNewRow.Cells.Add(MakeCell("ZOO ANIMALS", 3))
      myNewRow.Cells.Add(MakeCell("VARMINTS", 3))
      myNewRow.Cells.Add(MakeCell("GAME ANIMALS", 3))
      myTable.Rows.AddAt(0, myNewRow)
    End Sub
    
    Protected Function MakeCell(Optional ByVal text As String = "", Optional ByVal span As Int32 = 1) As TableHeaderCell
    ' { ColumnSpan = span, Text = text ?? string.Empty, CssClass = "table-header" }
      Dim header As New TableHeaderCell()
      header.ColumnSpan = span
      header.Text = text
      header.CssClass = "table-header"
      Return (header)
    End Function