Sunday, August 15, 2010

Replace a detail table control with a list control: CheckBoxList or DualList

Introduction

ISD's master-detail page supports adding/editing a master record and its detail records. However, the out-of-the-box detail table control is not the ideal presentation in some scenarios. Sometimes, a CheckBoxList is much clearer to present the information and easier for users to make selections, as described in Herman Chan's article, Implementing a Detail Table as CheckBox List. In some cases, a DualList might be the best choice as described in this article.

In both articles, the plumbing work of data loading and saving is encapsulated in custom user controls. User controls are easier to develop, but more difficult to deploy or use, than server controls. For example,
  • Maintain multiple source files within a project.
  • Maintain different versions (C# and VB) of source files for different projects. This may be not necessary for in-house development, but is likely for consulting projects.
  • Limited access to properties and methods for dynamically loaded user controls at run-time.
  • Limited access to inner properties at design-time.
It would be nice to see both implemented as server controls.

Solution

The most difficult part of implementing a server control is typically its rendering. You are not able to drag and drop components into a designer window, but have to code it line by line. However, using a technique described in Deveploy a Userver Control, you can design a server control with drag and drop just like a user control. In this article, I re-implemented CheckBoxList and DualList as server controls. The class diagram is shown below.

I am not going to reiterate the details about how they are implemented (you can get them from the above links). Instead, I will simply describe how to use them in your ISD projects.

Use the controls

Using the two controls is like using any other 3rd-party components.

Step 1: Add dll library

Download and unzip the demo at the end of this article. Find the following 3 dll files in the bin folder, and copy them to your project's bin folder.
  1. DetailListControl.dll
  2. DualListBox.dll
  3. UserverCtrl.dll
If your project is a "Web Application" project, you also need to explicitly add reference to the first dll (DetailListControl.dll). You don't have to explicitly reference the other two dlls. They are just dependencies.

Step 2: Add control to a page

First, register the assembly in page prologue:
<%@ Register Assembly="DetailListControl" Namespace="DingJing" TagPrefix="dj" %>
Next, insert the control in a cell editor. For example,


Step 3: Configure the control

Both CheckListDetailControl and DualListDetailControl inherit the following properties from the DetailListControl.
Property Required Description
DetailTableName Yes Name of detail table or view. For example, Developer_Language, Developer_DBMS.
DetailColumnName Yes Name of detail comlumn. For example, Language_ID, DBMS_ID.
WhereString No Filter on detail column's parent table or view. For example, Programming, DBMS.

WhereString can be set dynamically. The ideal place to set it at runtime is in the PrePopulate event handler. In addition to the WhereString property (of string type), you can also set the ItemFilter property (of WhereClause type) directly at runtime, as shown in the following code snippet.

C#
public DevelopersRecordControl() {
  Init += new EventHandler(DevelopersRecordControl_Init);
}

void DevelopersRecordControl_Init(object sender, EventArgs e) {
  ProgrammingDualList.PrePopulate += new EventHandler(ProgrammingDualList_PrePopulate);
}

void ProgrammingDualList_PrePopulate(object sender, EventArgs e) {
  WhereClause wc = new WhereClause(ProgrammingTable.Language_Name,
    BaseFilter.ComparisonOperator.Starts_With, "C");
  ProgrammingDualList.ItemFilter = wc;
}

VB
Public Sub New()
  Init += New EventHandler(AddressOf DevelopersRecordControl_Init)
End Sub

Private Sub DevelopersRecordControl_Init(sender As Object, e As EventArgs)
  ProgrammingDualList.PrePopulate += New EventHandler(AddressOf ProgrammingDualList_PrePopulate)
End Sub

Private Sub ProgrammingDualList_PrePopulate(sender As Object, e As EventArgs)
  Dim wc As New WhereClause(ProgrammingTable.Language_Name, BaseFilter.ComparisonOperator.Starts_With, "C")
  ProgrammingDualList.ItemFilter = wc
End Sub

The presentation components of the two controls are exposed as public properties, CheckList and DualList, respectively. So they can be configured declaratively or dynamically. For example,


  


Step 4: DataBind and SaveData

Each DetailListControl needs a line of code to be wired up with its master record at DataBind() or SaveData(). For example,

C#
public override void DataBind() {
  base.DataBind();

  if (DataSource != null) {
    ProgrammingDualList.DataBind(DataSource);
    DatabaseCheckList.DataBind(DataSource);
  }
}

public override void SaveData() {
  base.SaveData();

  ProgrammingDualList.SaveData(DataSource);
  DatabaseCheckList.SaveData(DataSource);
}

VB
Public Overrides Sub DataBind()
  MyBase.DataBind()

  If DataSource IsNot Nothing Then
    ProgrammingDualList.DataBind(DataSource)
    DatabaseCheckList.DataBind(DataSource)
  End If
End Sub

Public Overrides Sub SaveData()
  MyBase.SaveData()

  ProgrammingDualList.SaveData(DataSource)
  DatabaseCheckList.SaveData(DataSource)
End Sub

Conclusion

Reimplementated as server controls, the CheckListDetailControl and the DualListDetailControl are easier to (re)use than their user control versions.

Download

Demo application

5 comments:

Unknown said...

Thanks dingjing for sharing this very useful feature. I have downloaded the demo application and converted it o v9.2. It does compile without an error but opening EditDevelopers.aspx page in the browser gives an error (Unable to load on or more requested types) and the both listboxes are empty.

Is it possible to have the libraries for V9.2?

Ephrem

Unknown said...

The control is great, and you only need to include the System.Web.MVC version 1 into you bin Folder to be able to use the control in Later versions of Iron Speed.

The problem that i have is that the this new control does not have the ListWidth property, like the old ascx control that can steel be used in newer version. With the ListWidth and Amount of Row the Dual List would be a complete control. If you can publish the code we could recompile for newer versions without the MVC one reference and include this properties that i mention.

Thanks for a great implementation.

Unknown said...

The problem with later version of ISD is that the control requires the System.Web.MVC reference, just inlcude into the bin folder that library from the Dot.Net framework 3.5 and you will solve your problem.

My problem is that the control no longer has two attributes that the older ascx control has, those are the ListWidth and ListRows attibutes that let you control how the control looks, and the Lists are too narrow for longer names.

Please publish the source to include them or include them and post the dll separately to be able to use this control correctly.

Anonymous said...

The problem with later version of ISD is that the control requires the System.Web.MVC reference, just inlcude into the bin folder that library from the Dot.Net framework 3.5 and you will solve your problem.

My problem is that the control no longer has two attributes that the older ascx control has, those are the ListWidth and ListRows attibutes that let you control how the control looks, and the Lists are too narrow for longer names.

Please publish the source to include them or include them and post the dll separately to be able to use this control correctly.

Jing said...

This was developed long time ago. I can not find the source code. Please have a look at this post (http://dotnetslackers.com/articles/aspnet/Deveploy-a-Userver-Control.aspx) for a similar control.