It's not a full example with code downloads, but here's an example of late binding in C#, when we don't know whether a class being bound into a DataGrid is going to be of type Article, Vacancy, Image, or whatever. I've found this to be a good approach when customizing a DataGrid that we can re-use in our internally-developed content management system.

Since the objects we get back from NHibernate, wonderfully enough, are just objects of the type we ask for instead of some stupid intermediate typeless object contained in a DataSet, we can just bind them into the DataGrid. The one thing that's necessary is that we need to know anything going into the DataGrid needs to implement certain properties, like Title and Active.

First, we define an interface that content objects must implement:

CODE:
  1. using System;
  2. using System.Collections;
  3.  
  4. namespace com.headlondon.cms.models
  5. {
  6.         /// <summary>
  7.         /// IContent interface specifies the methods and properties that a piece of content must
  8.         /// implement.  Other classes, such as headcms.admin.common.controls.datagrid.HeadGrid
  9.         /// rely on these details to be able to do late binding. 
  10.         /// </summary>
  11.         interface IContent
  12.         {
  13.                 int Id { get; set; }
  14.                 string Title { get; set; }
  15.                 bool Active { get; set; }
  16.                 void Save();
  17.                 void Delete();
  18.         }
  19. }
  20.  

Then we make sure our concrete classes do in fact implement this interface:

CODE:
  1. using System;
  2. using System.Collections;
  3. using com.headlondon.cms.dataaccess;
  4.  
  5. namespace com.headlondon.cms.models
  6. {
  7.         /// <summary>
  8.         /// The base Article class representing HTML text content in the system. It can show up as-is, but is
  9.         /// also used by other classes such as Vacancy and FAQ for inheritance.
  10.         /// </summary>
  11.         public class Article : IContent
  12.         {
  13.                 private ArticleDao dao = new ArticleDao();
  14.  
  15.                 private int id;
  16.                 private string title;
  17.                 private string body;
  18.                 private bool active;
  19.  
  20.                 /// <summary>
  21.                 /// Constructor.
  22.                 /// </summary>
  23.                 public Article()
  24.                 {
  25.                         Id = -1;
  26.                         Active = false;
  27.                 }
  28.  
  29.                 /// <summary>
  30.                 ///  Save the article to the database.
  31.                 /// </summary>
  32.                 public void Save()
  33.                 {
  34.                         dao.Save(this);
  35.                 }
  36.  
  37.                 public static IList List()
  38.                 {
  39.                         ArticleDao dao = new ArticleDao();
  40.                         return dao.List(typeof(Article));
  41.                 }
  42.                 /// <summary>
  43.                 ///  List articles that share a property value.
  44.                 /// </summary>
  45.                 public static IList List(string property, object propertyValue)
  46.                 {
  47.                         ArticleDao dao = new ArticleDao();
  48.                         return dao.ListByPropertyValue(typeof(Article), property, propertyValue);
  49.                 }
  50.  
  51.                 /// <summary>
  52.                 ///  List articles that share a property, order by an orderPropertyName and a value "ASC" or "DESC".
  53.                 /// </summary>
  54.                 public static IList List(string property, object propertyValue, string orderPropertyName, string orderValue)
  55.                 {
  56.                         ArticleDao dao = new ArticleDao();
  57.                         return dao.ListByPropertyValueOrderBy(typeof(Article), property, propertyValue, orderPropertyName, orderValue);
  58.                 }
  59.  
  60.                 /// <summary>
  61.                 ///  List articles by an orderPropertyName, and a value "ASC" or "DESC".
  62.                 /// </summary>
  63.                 public static IList List(string orderPropertyName, string orderValue)
  64.                 {
  65.                         ArticleDao dao = new ArticleDao();
  66.                         return dao.List(typeof(Article), orderPropertyName, orderValue);
  67.                 }
  68.  
  69.                 /// <summary>
  70.                 ///  Get an article by its Id.
  71.                 /// </summary>
  72.                 public static Article Get(int id)
  73.                 {
  74.                         ArticleDao dao = new ArticleDao();
  75.                         return dao.Get(typeof(Article), id) as Article;
  76.                 }
  77.  
  78.                 /// <summary>
  79.                 ///  Delete the article object from the database.
  80.                 /// </summary>
  81.                 public void Delete()
  82.                 {
  83.                         dao.Delete(this);
  84.                 }
  85.  
  86.                 // Accessors
  87.  
  88.  
  89.                 /// <summary>
  90.                 /// The Article's Id.
  91.                 /// </summary>
  92.                 public int Id
  93.                 {
  94.                         get { return id; }
  95.                         set { id = value; }
  96.                 }
  97.  
  98.                 /// <summary>
  99.                 /// The Article's Title.
  100.                 /// </summary>
  101.                 public string Title
  102.                 {
  103.                         get { return title; }
  104.                         set { title = value; }
  105.                 }
  106.  
  107.                 /// <summary>
  108.                 /// The Article's Body text.
  109.                 /// </summary>
  110.                 public string Body
  111.                 {
  112.                         get { return body; }
  113.                         set { body = value; }
  114.                 }
  115.  
  116.                 /// <summary>
  117.                 /// Is this Article active?  I.e. does it show up on the public site?
  118.                 /// </summary>
  119.                 public bool Active
  120.                 {
  121.                         get { return active; }
  122.                         set { active = value; }
  123.                 }
  124.         }
  125. }

Then when we want to grab a piece of content from the database and stick it in a DataGrid using a DataBind method call, we don't have to worry about what its concrete class is, we just say something like this:

CODE:
  1.  
  2.                 /// <summary>
  3.                 /// Handler for the DataBinding event where we bind the data for a specific row
  4.                 /// to the CheckBox.
  5.                 ///
  6.                 /// Any type in the DataGrid must implement the interface IContent, which specifies that the
  7.                 /// item must have an Id, Active, and a bunch of other properties.
  8.                 /// </summary>
  9.                 private void BindData(object sender, EventArgs e)
  10.                 {
  11.                         CheckBox checkBox = (CheckBox)sender;
  12.                         DataGridItem container = (DataGridItem) checkBox.NamingContainer;
  13.                         IContent content = (IContent)container.DataItem;
  14.                         checkBox.Checked = content.Active;
  15.                         HtmlInputHidden oHid = (HtmlInputHidden)container.FindControl("oHid");
  16.                         oHid.Value = content.Id.ToString();
  17.                 }
  18.  

That code was from a custom CheckBoxItem class, the custom DataGrid class does something like this during its DataBind event:

CODE:
  1.  
  2.                                 IContent content = (IContent) e.Item.DataItem;
  3.                                 ((HyperLink)e.Item.Cells[0].FindControl("lblC1")).Text = content.Title;
  4.                                 ((HyperLink)e.Item.Cells[0].FindControl("lblC1")).NavigateUrl = formPath + content.Id;
  5.  

This blog post is just to put you on the right track, if you need more help with this sort of thing just reply in the comments. It's been a pleasant way to get a lot of functionality out of very little code.

Web References:

If you're not familiar with the basic concepts, there is a good discussion of late binding in C# and VB.NET here. Oddly it doesn't include this interface trick above, so maybe this will help someone.


One Response to “Late Binding of C# objects from NHibernate into the ASP.NET DataGrid control”  

  1. 1 Dave Hrycyszyn

    Just as a note: thinking about it some more, I really prefer this way of doing late binding to the loose-typing VB.NET way. The reason is pretty simple - there is absolutely no way to screw it up, due to type safety. In VB.NET, you could just say something like:

    Option Strict Off

    …and then proceed to access any property or method of the Article object (which is in fact just an object). The trouble would be that if you blew it and forgot to implement, let’s say, Title in one of your classes, you’d get a runtime error when a user tried to access your code. Using the IContent interface ensures that any class that implements IContent *must* have a Title, Id, etc. So, even in VB.NET, this technique of casting to an interface is still the way to go.

Leave a Reply