Posts
201
Comments
1111
Trackbacks
51
August 2009 Entries
MVC AJAX Form with Ajax.BeginForm() and jQuery Thickbox

A relatively common scenario you might want in your application is the ability for a user to click a link that pops up a little dialog to submit some information.  For example, let’s say you have this form where the user could click the “Contact this person” link:

 

After clicking this link, it pops up the following dialog where the user can type in their message:

 

Finally, once the user submits their message, it shows a little confirmation:

 

This scenario can be implemented with MVC with very few lines of code.  First off, we’ll be using the AJAX HTML Message pattern so that the AJAX messages that are going across the wire are simple HTML snippets.  Also, we’ll use jQuery Thickbox for our dialog. The first thing we need to do it to implement our “Contact this person” link:

   1:  <%=Html.ActionLink("Contact this person", "Index", "Contact", new { height = 200, width = 300 }, new { @class = "thickbox" }) %>

This is a pretty typical implementation of the jQuery thickbox.  We’re specifying that we want an AJAX call to be made to our ContactController.Index() method. The HTML that this ActionLink renders will simply look like this:

   1:  <a class="thickbox" href="/Contact?height=200&amp;width=300">Contact this person</a>

The complete code for the ContractController looks like this:

   1:  public class ContactController : Controller
   2:  {
   3:      public ActionResult Index()
   4:      {
   5:          return View(new ContactMessage());
   6:      }
   7:   
   8:      [ActionName("Index")]
   9:      [AcceptVerbs(HttpVerbs.Post)]
  10:      public ActionResult SubmitMessage(ContactMessage message)
  11:      {
  12:          return this.View("Confirmation", message);
  13:      }
  14:  }

It is important to note that both views returned by the ContactController’s action methods are both partial views (i.e., Index.ascx and Confirmation.ascx) with our HTML snippets in there. We know that in the Index.ascx we want to do an AJAX form submission/post rather than a full page form submission. There are a couple of ways to do this including using the jQuery Form plugin. However, in this case, the MVC framework already comes built-in with this functionality so we don’t have to rely on the jQuery Form plugin (unless we want to). Most of the time we find ourselves using the Html property of the View which is of type HtmlHelper.  However, there is also an Ajax property of the view of type AjaxHelper. The AjaxHelper has a BeginForm method that will allow you to submit the form via AJAX. You’ll need to make sure you include the MicrosoftAjax.js and MicrosoftMvcAjax.js scripts in your page to use the Ajax helpers.  The complete implementation for the Index.ascx can just look like this:

   1:  <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcAjaxForm.Models.ContactMessage>" %>
   2:   
   3:  <h2>Contact</h2>
   4:      
   5:  <% using (Ajax.BeginForm("Index", "Contact", new AjaxOptions { UpdateTargetId = "contactArea" } )) { %>
   6:      <div id="contactArea">
   7:          Email Address: <%=Html.TextBox("EmailAddress") %> <br /><br />
   8:          Message: <%=Html.TextArea("MessageText")%>
   9:          <input type="submit" />
  10:      </div>
  11:  <% } %>

Notice the UpdateTargetId is specifying the “contactArea” div.  This causes all HTML that is sent back on the form post (i.e., the confirmation message) to be displayed inside this div only.  The complete code for this sample can be downloaded here.

Posted On Monday, August 31, 2009 5:00 AM | Comments (3)
FredNUG Code Samples
Thanks to everyone who attended my presentation last night at FredNUG.  The code samples can be downloaded here.
Posted On Thursday, August 27, 2009 12:30 PM | Comments (0)
C# 3.0 Deep Dish with a slice of C# 4.0 – Code Samples

Thanks to everyone who attended my presentation last night at CapArea on C# 3.0 and C# 4.0.  You can download the C# 3.0 code samples, C# 4.0 code samples, and the PowerPoint presentation from here.

Several people asked me about some of the tools I was using during the presentation.  A list of the tools that I use (and more) can be found here.

Feel free to email me with any questions.

Posted On Wednesday, August 26, 2009 10:04 AM | Comments (2)
Rails for .NET Developers - Review

In an effort to continually improve as a developer, one of the things I do is read lots of books. Recently I read Rails for .NET Developers by Jeff Cohen and Brian Eng. Overall, I would definitely recommend this book to any ASP.NET developer that wants to learn more about Rails or wants to better understand the concepts behind MVC.

Given that ASP.NET MVC was heavily influenced by Ruby on Rails, I wanted to learn more about Rails so that I could have a better understanding of how MVC evolved the way it did and how it might potentially evolve in the future. The book did a very effective job explaining Rails concepts while drawing parallels to ASP.NET that I, as an ASP.NET developer, could easily grasp quickly. However, it was written when MVC was still in CTP form so almost all comparisons were from Rails to traditional ASP.NET (not MVC).  Rails is completely different from traditional ASP.NET but *incredibly* similar to ASP.NET MVC.  In fact, although I always knew that MVC was influenced by Rails, I never realized the extent of just how similar (I daresay identical in many areas) MVC and Rails are. I guess it’s true what they say: imitation is the sincerest form of flattery. :)

The book starts out giving a very high level overview of what a Rails application looks like – this provides a solid foundation for the rest of the book.  The next two chapters give a good overview of key concepts in the Ruby language. The book does not pretend to be a full-featured Ruby language reference – it introduces the major language features needed to get up to speed with Rails quickly.

The second section of the book (chapter 4-9), make up the major content of the book. It describes Rails’ approach to convention over configuration, REST, CRUD, Routes, Views, and more. I really liked the explanation of the way REST works in the context of Rails. The sidebar at the end of chapter 5 also gives a quick explanation for how Rails handles the PUT and DELETE verbs which is something I think Microsoft should consider baking into the MVC framework. I found the examples dealing with grid data to be pretty good – it’s always struck me as interesting that when a traditional ASP.NET developers sees a <% foreach %> in an MVC app for the first time, they freak out. But this is commonplace in the Rails world and not a big deal at all. As long as your view models are clean, I’ve always felt that using a simple <% foreach %> loop with intellisense for your properites is still more desireable than <% Eval(“someProperty”) %> but I digress. The chapter on layouts and partial views was quite interesting and you can see the influence of the Rails partial in the MVC2 EditorFor concept. The layouts versus MasterPages discussion was interesting and, while there are many similarities, this was an area where I felt MasterPages shined.

The final section of the book dealt with TDD, integration with .NET, RubyGems, and future directions. These chapters were all of a very introductory nature but the TDD chapter did make me want to learn more about using RSpec with IronRuby (though the examples in the book were with Shoulda, not RSpec). Though there were only 5 pages at the end of the book that discussed ASP.NET MVC, it does make me wonder if they’ll eventually put out a second edition of the book that uses MVC comparisons throughout.

In general, there are several reasons why I think it’s a good idea to at least be familiar with Ruby on Rails.  First, getting a better technical breadth is always helpful to making you a more well-rounded developer. Also, like me, you may be a developer that has no plans to move away from the Microsoft platform in the near future – but leveraging the best practices and concepts from other platforms can improve your own code as well (translation: we can learn a lot from Rails developers). Microsoft recognizes this and it’s the reason they built MVC and IronRuby. The last few years have seen a trend towards dynamic languages like Ruby and IronRuby will soon make the Ruby language a first class citizen in the .NET framework. Before you know it, you could find yourself using IronRuby with MVC.

Posted On Sunday, August 23, 2009 8:14 AM | Comments (1)
C# 3.0 Deep Dish with a slice of C# 4.0

On Tuesday night (August 25), I will be presenting “C# 3.0 Deep Dish with a slice of C# 4.0” at CapArea .NET User Group (but I have to give Brian Noyes credit for the catchy title). The primary content of the presentation will be a deep dive into the C# 3.0 language features. The latter part of the presentation will be an overview of the features coming in C# 4.0. Hope to see you there.

Additionally, the next night (Wednesday, August 26), I’ll be at FredNUG presenting Linq to SQL.

Posted On Sunday, August 23, 2009 7:44 AM | Comments (0)
Velocity CTP3 Set up for ASP.NET Session State

Historically I have always viewed ASP.NET session state as “pure evil.” In-proc session state is about as unreliable as you can get given that you can have timeouts, ASP.NET might recycle itself, IIS might get bounced, no scalability, and cannot work in a web farm.  SQL Session state is very resilient and works in web farm scenarios but the performance is not good and at that point you might as well be better off writing your own strongly-typed data access layer rather than blobbing everything in and out of the database.  ASP.NET state server is the “middle man” of the various options but it’s not very fault tolerant. As part of Microsoft’s Velocity distributed cache they are providing an asp.net session state provider that is actually a *very* compelling option in my opinion for managing asp.net session state – more compelling than any of the traditional three choices.  It’s an in-memory distributed cache and it provides *both* high performance as well as robust fault tolerance potentially giving the best of both worlds.

I recently set up Velocity CTP3 in my development environment and there were a couple of minor gotchas.  Many of the samples on the web were either incomplete or outdated (i.e., based on previous versions prior to CTP 3).  The links I found most helpful (in addition to the Velocity help file) to get everything set up were:  Build Better Data-Driven Apps With Distributed Caching, How to: Configure a Session State Provider, and Andrea Col’s Blog.  Putting all these links together, I was able to get it working.  This post will walk through all of the set up steps in a single place.

Step 1 – Download and run installer

Download Velocity CTP 3 from this location.

Step 2 – Accept firewall defaults

Step 3 – Cache Host Configuration

Create a new database called “Velocity” in SQL Server Management Studio. On the Cache Host Configuration screen, specify these values:

  • Storage Location type: SQL Server database
  • Connection string: data source=<YourDBServer>;Initial Catalog=Velocity;Integrated Security=SSPI
  • Cluster Name: “DevCluster” (this could really be anything)
  • Cluster Size:  Small (1-4)

Leave all other defaults.

 

Step 4 – Configure with PowerShell

Start PowerShell Administration Tool found in the “Microsoft Distributed Cache” fold in Program Files off the Start menu.  The first time you run it can you choose “A” for always run software from this untrusted publisher.

Execute:  Start-CacheCluster

Execute:  New-Cache -CacheName ASPNETSessionCache -TTL 60

For verification you can look at the statistics for the cache you just created:

Execute:  Get-CacheStatistics -CacheName ASPNETSessionCache

 

Step 5 – Configure web.config

The best reference I found for the web.config for CTP3 is here.  You can copy/paste that and either leave as is or tweak if desired.  For example, you might want to change config values in the sessionState section for cookieless, etc. depending on your scenario.

 

Hope this helps ease the set up process.

Posted On Friday, August 14, 2009 3:46 AM | Comments (2)
MVC RadioButtonList HTML Helper – Take 3

In two previous posts, I talked about ways to create your own HTML Helper to generate a radio button list.  In the first post I leveraged the FluentHtml library to create a table layout.  In the second post I switched this to using a div instead of a table.  The crux of the issue was that I wanted to make sure the the “id” and “name” attributes were set correctly and MVC out of the box doesn’t seem to do this properly.  Recently I discovered a way to successfully do this without “wrapping” the FluentHtml library but by using it directly while leveraging a “RadioSet” like this:

   1:  <%=this.RadioSet(m => m.Name).Options(Model.FooBarList) %>

To produce HTML that looks like this:

   1:  <div id="Name">
   2:      <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
   3:      <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
   4:  </div>

which is exactly what I want.  However, there are a couple of gotchas I ran into for making this work properly.  First off, it’s important to note that the Options() method above is taking an IEnumerable<SelectListItem> and FooBarList is IEnumerable<SelectListItem>.  The first thing I tried was creating my list like this:

   1:  private static IEnumerable<SelectListItem> CreateFooBarList()
   2:  {
   3:      var list = new List<string> { "Foo", "Bar" };
   4:      return new SelectList(list);
   5:  }

This resulted in HTML that was broken as the id’s and value’s were not set correctly:

   1:  <div id="Name">
   2:      <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Foo</label>
   3:      <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Bar</label>
   4:  </div>

So realized it was probably because that SelectListItem constructor just didn’t assign the Value property when a list of strings was sent in.  So I changed to this:

   1:  private static IEnumerable<SelectListItem> CreateFooBarList()
   2:  {
   3:      var list = new List<SelectListItem>
   4:      {
   5:          new SelectListItem { Text = "Foo", Value = "Foo" },
   6:          new SelectListItem { Text = "Bar", Value = "Bar" }
   7:      };
   8:      return new SelectList(list);
   9:  }

I figured now that I was using an actual strongly-typed SelectList I would be all set.  But unfortunately, the rendered HTML was even worse now:

   1:  <div id="Name">
   2:      <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
   3:      <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
   4:  </div>

It turns out that the way I had to fix this was to specify *all* arguments on the SelectList constructor with the dataTextField and dataValueField as shown on line #8 below:

   1:  private static IEnumerable<SelectListItem> CreateFooBarList()
   2:  {
   3:      var list = new List<SelectListItem>
   4:      {
   5:          new SelectListItem { Text = "Foo", Value = "Foo" },
   6:          new SelectListItem { Text = "Bar", Value = "Bar" }
   7:      };
   8:      return new SelectList(list, "Value", "Text");
   9:  }

Now the HTML and all attributes are correct.  You would *think* that the constructor of the SelectList would be smart enough to identify that the IEnumerable collection that was sent in was of type IEnumerable<SelectListItem> (which is what it ultimately wants) and set those arguments for you automatically. But it looks like that was a naive assumption on my part.  Easy enough to add a simple extension method to do this for you throughout your codebase. Make sure to watch out for this in your own code – I won’t make this mistake again.

Posted On Friday, August 7, 2009 3:33 AM | Comments (4)
Alternate MVC Screen Navigation

Suppose you have a simple sequential screen flow in your application like this:

One typical scenario for this is that each screen has its own associated Controller (for the example we’ll say Controller1, Controller2, etc. but obviously they would have meaningful names in the real world).  When the user submits Screen1 then the code Controller 1 will have something like this:

   1:  return this.RedirectToAction("Index", "Controller2");

In fact, each controller would have to have code like this that essentially “points to” the next controller in the sequential screen navigation. But if Screen 2 and Screen 4 swapped positions in the order, you’d have to:

  • Change the code in Screen 1 to point to Screen 4 instead of 2
  • Change the code in Screen 4 to point to Screen 3 instead of 5
  • Change the code in Screen 2 to point to Screen 5 instead of 3

So in a case like this, it might make more sense to encapsulate this sequential order in a single place.  The goal would be to be able to change the code in each controller to something like this:

   1:  return this.RedirectToNextAction();

It’s actually not very much work to implement something like this.  First, you’d want to encapsulate everything in some type of “PageManager” class that has a GetNextStepFrom() method where you give it a controller name and it gives you back the next controller.  I’ve oversimplified an example in the interests of keeping this example simple:

   1:  class PageManager
   2:  {
   3:      public string GetNextStepFrom(string controller)
   4:      {
   5:          int index = controllerList.IndexOf(controller);
   6:          return controllerList[index + 1];
   7:      }
   8:   
   9:      private List<string> controllerList = InitializeControllerSequence();
  10:   
  11:      private static List<string> InitializeControllerSequence()
  12:      {
  13:          return new List<string> {
  14:              "Controller1",
  15:              "Controller2",
  16:              "Controller3",
  17:              "Controller4",
  18:              "Controller5"
  19:          };
  20:      }
  21:  }

At this point, we’re ready to implement our RedirectToNextAction() method which is what makes all this work. The best way is to sub-class all your controllers and put this protected method in the base class:

   1:  protected ActionResult RedirectToNextAction()
   2:  {
   3:      var controllerName = this.RouteData.Values["Controller"].ToString();
   4:      var nextController = PageManager.GetNextStepFrom(controllerName);
   5:      return this.RedirectToAction("Index", nextController);
   6:  }

Notice that to get the name of the current controller, I’m having to get it from the RouteData dictionary.  What would be really nice if I could just do this.Name to get the name of the controller.  If you use the MVC T4 templates, it does give each controller class a Name property (although right now it’s actually a public field) but it’s not usable for this implementation in its current format because the Name property is not on the base class so can’t be used polymorphically.  Note that in C# 4.0, you could rewrite line #3 above to be:

   1:  dynamic thisController = this;
   2:  var controllerName = thisController.Name;

This would be leveraging a form of duck typing but may be overkill for a scenario like this since you’re essentially relying on Reflection at this point.

In any case, scenarios like these are good candidates for encapsulating the concern of screen navigation flow to an independent component outside the controllers.

Posted On Friday, August 7, 2009 2:08 AM | Comments (0)
Interesting LINQ Exercise

A co-worker posed an interesting LINQ problem to me tonight so I figured I’d share.  They had a collection of items and wanted an algorithm that would create a “collection of collections” where the first three items would be grouped together, second three items, on so on.  For example, given a sequence like this: { “a”, “b”, “c”, “d”, “e”, “f”, “g”, “h” }, it would create a structure that contained 3 groups – the first element would be { “a”, “b”, “c” }, the second would be { “d”, “e”, “f” } and the third { “g”, “h” }. They already had an algorithm working fine but it was using a “brute force” approach and took about 15-20 lines of code.  It “felt like” you could solve the same problem more elegantly with a LINQ solution and with less lines of code.

Here was my first solution:

   1:  var list = new List<string> { "a", "b", "c", "d", "e", "f", "g", "h" };
   2:  var results = new List<IEnumerable<string>>();
   3:  int numBlocks = list.Count % 3 == 0 ? list.Count / 3 : (list.Count / 3) + 1;
   4:   
   5:  for (int i = 0; i < numBlocks; i++)
   6:  {
   7:      results.Add(list.Skip(i * 3).Take(3));
   8:  }

First, I have to figure out the number of “blocks” or elements that the structure (the collection of collection) should have.  Next, for each “block position” I leverage the LINQ Skip method in conjunction with the Take method (essentially like a paging operation).  I was reasonably happy with the first solution but I kept feeling like I could do it with less lines of code and without the “for” loop.  The problem with eliminating the for loop is that it was providing the outer loop and with LINQ you basically have to loop over something.  After a little digging, I came across the Enumerable Range method which I hadn’t used before.  This allowed me to solve the problem with 1 line of code:

   1:  var list = new List<string> { "a", "b", "c", "d", "e", "f", "g", "h" };
   2:  int numBlocks = list.Count % 3 == 0 ? list.Count / 3 : (list.Count / 3) + 1;
   3:  var results = Enumerable.Range(0, numBlocks).Select(i => list.Skip(i * 3).Take(3)).ToList();

Now we’ve got our collection of collection and can iterate over it:

   1:  foreach (var item in results)
   2:  {
   3:      foreach (var innerItem in item)
   4:      {
   5:          Console.WriteLine(innerItem);
   6:      }
   7:  }

While I was originally happy that I found a way to do it with 1 line of code, it’s not really a great solution because it’s just a little “too clever”.  The for loop is going to be easier to understand for the poor developer that comes behind you and has to maintain your code.  Also, the second solution is not as efficient because the Range method creates an array just so i can loop over it just so it can make it workable in LINQ.  Bottom line, the first solution is better.  But of course, if someone has an even better solution, please do not hesitate to share it in the comment section below.

To take it one step further, the LINQ SelectMany method will allow you to take the collection and collections and flatten it back out to the original single collection like this:

   1:  var flattened = results.SelectMany(x => x);
Posted On Thursday, August 6, 2009 4:24 PM | Comments (10)
MVC RadioButtonList HTML Helper – Take 2

Yesterday I posted a solution for creating a RadioButtonList HTML helper.  But upon closer examination, I’ve come to the conclusion that for a horizontal flow of radio buttons, an HTML table is really not the way to go.  Perhaps a remnant from the days of web forms past. Based on a conversation I had with a co-worker, HTML output like this would be much better:

   1:  <div>
   2:      <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
   3:      <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
   4:  </div>

Not only does this produce better HTML, it also allows me to simplify the C# code:

   1:  public static string RadioButtonList<T>(this IViewModelContainer<T> container, Expression<Func<T, object>> expression, IEnumerable<SelectListItem> items) where T : class
   2:  {
   3:      TagBuilder divTag = new TagBuilder("div");
   4:   
   5:      foreach (var item in items)
   6:      {
   7:          var radioTag = container.RadioButton(expression).Value(item.Value ?? item.Text).Checked(item.Selected).LabelAfter(item.Text);
   8:          divTag.InnerHtml += radioTag;
   9:      }
  10:      return divTag.ToString();
  11:  }

This still gives me all the benefits from the original post including correct “id” and “name” attributes while being able to call it in one line of code like this:

   1:  <%=this.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>
Posted On Thursday, August 6, 2009 8:36 AM | Comments (2)
C# 4.0 dynamic keyword with IronRuby

In my continued exploration of C# 4.0, I wanted to put together a simple example of using the C# 4.0 “dynamic” keyword in conjunction with IronRuby.  I’ve been shocked at how difficult it was to find code samples on the internet that actually do this.  With much persistence (and some significant pointing in the right direction from Justin Etheredge) I got my simple example working.  Given that there aren’t many great examples of calling IronRuby from C# 4.0 out there, I figured I better post the code.

First off, you need to get the *correct* version of IronRuby that will work with VS2010 Beta 1.  Next, you want to add the following references to your project (all found in the “bin” folder of the IronRuby download):

  • IronRuby.dll
  • IronRuby.Libraries.dll
  • Microsoft.Scripting.dll

Next, create an IronRuby file called “person.rb” that has the following code:

   1:  class Person
   2:      def say_hello
   3:          puts "Hello world!"
   4:      end
   5:  end

Finally, here is the C# code:

   1:  ScriptRuntime runtime = IronRuby.Ruby.CreateRuntime();
   2:  ScriptScope scope = runtime.ExecuteFile("person.rb");
   3:  dynamic person = scope.Engine.Execute("p = Person.new");
   4:  person.say_hello();

Notice the use of the “dynamic” keyword on line 3.  While a full discussion of the dynamic keyword is beyond the scope of this blog post, this keyword is the basis for the dynamic features of C# 4.0 that will be running on top of the Dynamic Language Runtime (DLR) of .NET 4.0.  It will provide a unified way to interact with all things dynamic.  These include:

  • Dynamic languages like IronRuby, IronPython, and JavaScript
  • Simplified .NET Reflection
  • COM Interop
  • DOM objects like HMTL and XML

I’ll be speaking at CapArea at the end of this month where the last portion of my talk will be dedicated to C# 4.0 new language features.  Additionally, I’ll be devoting an entire talk to C# 4.0 at CMAP in November.

Posted On Wednesday, August 5, 2009 2:51 PM | Comments (1)
MVC RadioButtonList HTML Helper

The MVC framework does not come with a built-in RadioButtonList comes standard in ASP.NET web forms.  The MVC Futures assembly (which can be downloaded here) does come with this HtmlHelper but there are some issues with it including the ones described here by Elijah Manor.  He concludes that the method in the futures assembly isn’t very useful and states, “To get around this I ended up writing a foreach creating individual Html.RadioButton and labels.”  However, he did not show the code he wrote to implement it.  I came to a similar conclusion as Elijah so I figured I’d share the code and discuss a couple of interesting issues.

First off, my main goal was to be able to use a Helper that looked something like this:

   1:  <%=Html.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

to produce HTML that looked something like this:

   1:  <table>
   2:      <tr>
   3:          <td>
   4:              <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
   5:          </td>
   6:          <td>
   7:              <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
   8:          </td>
   9:      </tr>
  10:  </table>

This output is very similar to the output produced by the horizontal layout of the web forms RadioButtonList server control – of course if you want to use a non-table approach, have at it! 

Originally, I looked to build a typical HTML helper method internally leveraging the already built HTML RadioButton extension methods that ship with MVC. However, the primary thing I found odd is there does not seem to be a way to create a radio button control where the “id” and “name” attributes are different values (as shown in the above HTML). (Note: if someone knows a way around this, please let me know!)  On the other hand, the controls that come in FluentHtml library of MvcContrib does allow these attributes to correctly have different values.  I *highly* recommend trying out the FluentHtml library as it is a much nicer way (in my opinion) to create views than the standard HTML helpers that ship with MVC. So I built some HTML helpers that leverage the FluentHtml library rather than the built-in library.  The primary difference when authoring the custom HTML helper methods is that the FluentHtml methods extend IViewModelContainer<T> rather than the HtmlHelper class.  This translates to the extension methods hanging off the “this” pointer rather than the Html property of the view page.  My code for the RadioButtonList extensions was:

   1:  public static string RadioButtonList<T>(this IViewModelContainer<T> container, Expression<Func<T, object>> expression, IEnumerable<string> items) where T : class
   2:  {
   3:      var func = expression.Compile();
   4:      var result = func(container.ViewModel);
   5:      var selectList = new SelectList(items, result);
   6:      return container.RadioButtonList(expression, selectList);
   7:  }
   8:   
   9:  public static string RadioButtonList<T>(this IViewModelContainer<T> container, Expression<Func<T, object>> expression, IEnumerable<SelectListItem> items) where T : class
  10:  {
  11:      TagBuilder tableTag = new TagBuilder("table");
  12:      tableTag.AddCssClass("radio-main");
  13:   
  14:      var trTag = new TagBuilder("tr");
  15:      foreach (var item in items)
  16:      {
  17:          var tdTag = new TagBuilder("td");
  18:          var radioTag = container.RadioButton(expression).Value(item.Value ?? item.Text).Checked(item.Selected).LabelAfter(item.Text);
  19:          tdTag.InnerHtml = radioTag.ToString();
  20:   
  21:          trTag.InnerHtml += tdTag.ToString();
  22:      }
  23:      tableTag.InnerHtml = trTag.ToString();
  24:   
  25:      return tableTag.ToString();
  26:  }

Notice on line #18, I am taking advantage of the fluent interface enabled by the FluentHtml library to chain the method calls together. Also, I provide an overload that that you can use either IEnumerable<SelectListItem> or IEnumerable<string>.  Taking advantage of the SelectListItem semantics enables the ability to leverage the “Selected” property of the SelectListItem instance since I’m compiling the expression on line #3 above that was sent in to get the current value of the model’s property. This enabled me to now have this 1 line of code in my view:

   1:  <%=this.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

If you do not want to use the FluentHtml library, you can still come close to this with the built in extension methods.  The only thing lacking is that the HTML produced will have the same values for the “name” and “id” attributes which is really not technically correct. Also, there’s more manual work to create the “label” tag.  The code using the built-in methods is:

   1:  public static string RadioButtonList2(this HtmlHelper helper, string name, IEnumerable<string> items) 
   2:  {
   3:      var selectList = new SelectList(items);
   4:      return helper.RadioButtonList2(name, selectList);
   5:  }
   6:   
   7:  public static string RadioButtonList2(this HtmlHelper helper, string name, IEnumerable<SelectListItem> items) 
   8:  {
   9:      TagBuilder tableTag = new TagBuilder("table");
  10:      tableTag.AddCssClass("radio-main");
  11:   
  12:      var trTag = new TagBuilder("tr");
  13:      foreach (var item in items)
  14:      {
  15:          var tdTag = new TagBuilder("td");
  16:          var rbValue = item.Value ?? item.Text;
  17:          var rbName = name + "_" + rbValue;
  18:          var radioTag = helper.RadioButton(rbName, rbValue, item.Selected, new { name = name });
  19:          
  20:          var labelTag = new TagBuilder("label");
  21:          labelTag.MergeAttribute("for", rbName);
  22:          labelTag.MergeAttribute("id", rbName + "_label");
  23:          labelTag.InnerHtml = rbValue;
  24:   
  25:          tdTag.InnerHtml = radioTag.ToString() + labelTag.ToString();
  26:   
  27:          trTag.InnerHtml += tdTag.ToString();
  28:      }
  29:      tableTag.InnerHtml = trTag.ToString();
  30:   
  31:      return tableTag.ToString();
  32:  }

The important thing is that this type of pattern can be used to build your own re-usable libraries in your own apps.  Perhaps you don’t want a horizontal layout or you don’t want to use HTML tables. The above approach can be customized to your suit needs.

Posted On Wednesday, August 5, 2009 8:09 AM | Comments (14)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud