Jun 20

Part 1 discussed the benefits of a strongly typed view and part 2 went through the typical usage of the Mvc Contrib project’s fluent HTML library. Now we’ll take a look at a neater usage for it – validation.

Each extension in the Mvc Contrib fluent HTML library takes “behaviors” into account, applying all known behaviors first when rendering themselves to strings. These behaviors are defined by the following interface:

public interface IMemberBehavior : IBehaviorMarker
{
	void Execute(IMemberElement element);
}

Very simple. The interface defines a single method that takes in an IMemberElement, which provides the LINQ expression being used to render the control and also inherits from the IElement interface, which is the base interface all the HTML extensions end up deriving from:

public interface IElement
{
	/// <summary>
	/// TagBuilder object used to generate HTML.
	/// </summary>
	TagBuilder Builder { get; }
 
	/// <summary>
	/// How the tag should be closed.
	/// </summary>
	TagRenderMode TagRenderMode { get; }
 
	/// <summary>
	/// Set the value of the specified attribute.
	/// </summary>
	/// <param name="name">The name of the attribute.</param>
	/// <param name="value">The value of the attribute.</param>
	void SetAttr(string name, object value);
 
	/// <summary>
	/// Set the value of the specified attribute.
	/// </summary>
	/// <param name="name">The name of the attribute.</param>
	string GetAttr(string name);
 
	/// <summary>
	/// Remove an attribute.
	/// </summary>
	/// <param name="name">The name of the attribute to remove.</param>
	void RemoveAttr(string name);
 
	/// <summary>
	/// The text for the label rendered before the element.
	/// </summary>
	string LabelBeforeText { get; set; }
 
	/// <summary>
	/// The text for the label rendered after the element.
	/// </summary>
	string LabelAfterText { get; set; }
 
	/// <summary>
	/// The class for labels rendered before or after the element.
	/// </summary>
	string LabelClass { get; set; }
}

This allows a behavior to do basic modifications to an element, including changing HTML attributes and adding text before/after the element. More advanced operations can be done by breaking polymorphism and checking what type the IElement parameter actually is. Mvc Contrib bundles in one behavior by default, the ValidationBehavior, which checks ASP.NET MVC’s ModelState for any possible errors to display, effectively making the Mvc Contrib’s HTML helpers act the same as the built in ASP.NET MVC’s when it comes to error handling.

You’ll notice above I said each HTML extension takes all “known” behaviors into account when rendering themselves to strings. So how do the extensions learn about these behaviors? Simply put, the ModelViewPage we had our view page inherit from takes an array of them in one of its constructor overloads. The default constructor only loads the ValidationBehavior. All we need to do is create our own view base page, inherit from the ModelViewPage, and pass in our own behaviors like so:

public class FluentViewPage<T> : ModelViewPage<T> where T : class
{
	public FluentViewPage()
		: base(new MaxLengthBehavior())
	{
	}
}

There’s really not much to the above behavior, either. Simply have it inherit from IMemberBehavior and you’re good to go. Here it is in all its entirety:

public class MaxLengthBehavior : IMemberBehavior
{
	public void Execute(IMemberElement element)
	{
		var attribute = new MemberBehaviorHelper<StringLengthAttribute>().GetAttribute(element);
 
		if(attribute != null && element is ISupportsMaxLength)
		{
			element.SetAttr(HtmlAttribute.MaxLength, attribute.MaximumLength);
		}
	}
}

The passed in member element knows the property specified in the this.TextBox(x => x.Name) call (Name, in this case), as well as all the elements defined on the IElement interface above. We take that and use a helper class to pull the desired attribute off the passed in property. If it has the attribute we’re looking for, and supports setting a maximum length (checking using the ISupportsMaxLength marker interface so we’re not trying to somehow set the max length on a checkbox, for example), we set the max length HTML attribute on the element. Pretty simple, but again, this is only really scratching the surface of what can be done with these behaviors.

So with all this in place, simply slap the StringLenght attribute on any needed view model properties. For instance, if we modify the CustomerViewModel’s name property like this:

[StringLength(30)]
public string Name { get; set; }

The outputted HTML would be:

<input id="Name" maxlength="30" name="Name" type="text" value="John" />

Neat, huh? In our current project, we’re using these to automatically denote required fields, and in conjunction with xVal, require those fields complete with client side checks. All from a few simple attributes.

A zip of the final project can be found here, for reference.

Jun 17
TortoiseSVN 1.6+ Hot Keys
icon1 Darrell Mozingo | icon2 Quickie | icon4 June 17th, 2009| icon3No Comments »

The latest releases of Tortoise SVN (above 1.6) have taken away the ALT+O hot keys that used to be on every dialog (though it sounds like they’ve added them back in to the trunk). This caused quite an uproar by many keyboarders, myself included.

Until the trunk is stable and the ALT+O keys are reintroduced, though, know that CTRL+ENTER will substitute on pretty much all the dialogs (or ESC for Cancel). These are the “standard” Windows OK & Cancel shortcuts, and apparently the custom accelerator keys are hold overs from legacy WinAPI code.

I’ve grown accustomed to CTRL+ENTER at this point, but I’ll be happy to hopefully have my old friend, ALT+O, back soon.

Jun 10
Code & Coffee – Getting Started
icon1 Darrell Mozingo | icon2 Events | icon4 June 10th, 2009| icon3No Comments »

We had a couple of new guys join us for last week’s Code & Coffee, and during the 15 minutes or so we spent helping get them up to speed I realized a quick “getting started” post might be in order to help future attendees.

For starters, download the Ruby 1.8.6 one-click installer (from the main Ruby site). It may or may not require a reboot to get the standard C:\ruby install directory into your PATH variable. Open a command prompt and type ruby --version to make sure it’s working correctly.

We’re working our way through Edge Case’s Ruby Koans right now, which are basically a whole suite of failing unit tests that teach you more about the language as you get them passing. There’s a download link in the middle of the upper portion of their GitHub page. Once you download and extract it somewhere, open up a command prompt in the root directory and run the rake command. It will use the Rakefile in that directory by default and it should tell you the first test failed. Open the respective file in the koans directory, try getting it to pass, and re-run rake. Keep going through that process, test by test. Some are blatantly obvious, while others require some research. It’s best if you think about what you’re actually doing too, besides just trying to make the test pass. We’re currently somewhere in the about_hashes.rb file.

The idea is to get through these, soaking up as much as we can, then probably jump into an intro Rails application, eventually working on some sort of blog for the Code & Coffee. Should be fun. I’d also like to get into either RSpec or Cucumber along the way, and see what TDD/BDD in Ruby is like as I’ve always heard how great it is.

Our next get together will be Friday, June 19th @ 7am. Hope to see you there, and let me know if you have any problems with getting this stuff setup.