Display an Iron Speed Designer Record Panel in Popup

Learn how to display an Iron Speed Designer record panel in the built-in AJAX popup.
- Herman Chan, Founder of Presence Consulting Group

July 7, 2009
Iron Speed Designer V6.X

Introduction

The record panel and the AJAX popup are two outstanding Iron Speed Designer features. A record panel allows developers to display a database record in a professional-looking panel, in most cases without the need to write a single line of custom code. The AJAX popup enables pages to deliver large amounts of text and/or images on demand. This makes it easier to lay out pages and reduce the initial page load time.

Out-of-the-box a popup will display either text or image from a single database field, but not both. It is possible to display both text and an image (i.e. a record panel) in the popup with just a few lines of custom code. This article will show you how to customize your application to display an Iron Speed Designer record panel in the built-in AJAX popup.


Where does this come from?

This idea was inspired by ScottGu’s Blog: Cool UI Templating Technique to use with ASP.NET AJAX for non-UpdatePanel scenarios. We will design the record panel as a UserControl (.ascx), instead of an .aspx page. When the panel is requested on AJAX calls, use the UserControl to render the panel, and return its HTML content as a string.

Implementation

Step 1: Create a web service
Under the website root folder, we create a new web service using Microsoft Visual Studio’s Add New Item wizard. Visual Studio will create a template .asmx file under the website root folder, and a template C# (or Visual Basic .NET) file under App_Code folder. Insert the following code into the appropriate file.


C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.HtmlControls;
 
using BaseClasses.Data;
using BaseClasses.Utils;
 
using PopupPanelDemo.UI;
 
/// <summary>
/// Summary description for RecordPanelService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class RecordPanelService : System.Web.Services.WebService {
 
  private static string businessLayerPrefix = "PopupPanelDemo.Business.";
 
  public RecordPanelService() {
 
    //Uncomment the following line if using designed components
    //InitializeComponent();
  }
 
  [WebMethod(EnableSession = true)]
  public string GetRecordPanel(string tblName, string pnlPath, string rcdId) {
    // Find the App_Code assembly.
    var appAssm = (from a in AppDomain.CurrentDomain.GetAssemblies()
                   where a.FullName.StartsWith("App_Code")
                   select a).First();
 
    // Find the table or view class
    string tblTypeFullName = businessLayerPrefix + tblName + "Table";
    string tblQualifiedName = Assembly.CreateQualifiedName(appAssm.FullName,
    tblTypeFullName);
    Type tblType = Type.GetType(tblQualifiedName);
    if (tblType == null) {
      tblTypeFullName = businessLayerPrefix + tblName + "View";
      tblQualifiedName = Assembly.CreateQualifiedName(appAssm.FullName, tblTypeFullName);
      tblType = Type.GetType(tblQualifiedName);
      if (tblType == null)
        throw new Exception(string.Format("Cannot find class {0}Table or {0}View.", tblName));
    }
 
    // Retrieve the record
    PrimaryKeyTable tblInstance = tblType.GetField("Instance").GetValue(null) as
    PrimaryKeyTable;
    PrimaryKeyRecord rcd = tblInstance.GetRecordData(rcdId, false) as PrimaryKeyRecord;
 
    // Create a blank page and load the record panel user control.
    Page pg = new BaseApplicationPage();
    pg.EnableViewState = false;
    Control panel = pg.LoadControl(pnlPath);
    Control rcdCtrl = MiscUtils.FindControlRecursively(panel, string.Format("_{0}RecordControl",
    tblName));
 
    // Bind data to the panel
    rcdCtrl.GetType().GetProperty("DataSource").SetValue(rcdCtrl, rcd, null);
    rcdCtrl.DataBind();
 
    // Render the page
    HtmlForm tempForm = new HtmlForm();
    tempForm.Controls.Add(panel);
    pg.Controls.Add(tempForm);
    StringWriter sw = new StringWriter();
    try {
      HttpContext.Current.Server.Execute(pg, sw, false);
    } catch (Exception ex) {
      string msg = ex.Message;
    }
 
    // Strip out the panel html
    string outputToReturn = sw.ToString();
    outputToReturn = outputToReturn.Substring(outputToReturn.IndexOf("<div%gt;"));
    outputToReturn = outputToReturn.Substring(0, outputToReturn.IndexOf("</form>"));
    return outputToReturn;
  }
 
}

Visual Basic .NET
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Reflection
Imports System.Web
Imports System.Web.Services
Imports System.Web.UI
Imports System.Web.UI.HtmlControls
 
Imports BaseClasses.Data
Imports BaseClasses.Utils
 
Imports PopupPanelDemo.UI
 
''' <summary>
''' Summary description for RecordPanelService
''' </summary>
' To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
<WebService([Namespace] := "http://tempuri.org/")> _
<WebServiceBinding(ConformsTo := WsiProfiles.BasicProfile1_1)> _
<System.Web.Script.Services.ScriptService()> _
Public Class RecordPanelService
  Inherits System.Web.Services.WebService
 
  Private Shared businessLayerPrefix As String = "PopupPanelDemo.Business."
 
  Public Sub New()
 
    'Uncomment the following line if using designed components
    'InitializeComponent();
  End Sub
 
  <WebMethod(EnableSession := True)> _
  Public Function GetRecordPanel(ByVal tblName As String, ByVal pnlPath As String, ByVal rcdId
  As String) As String
    ' Find the App_Code assembly.
    Dim appAssm = (From a In AppDomain.CurrentDomain.GetAssemblies() _
      Where a.FullName.StartsWith("App_Code") _
      Select a).First()
 
    ' Find the table or view class
    Dim tblTypeFullName As String = businessLayerPrefix + tblName & "Table"
    Dim tblQualifiedName As String = Assembly.CreateQualifiedName(appAssm.FullName,
    tblTypeFullName)
    Dim tblType As Type = Type.[GetType](tblQualifiedName)
    If tblType Is Nothing Then
      tblTypeFullName = businessLayerPrefix + tblName & "View"
      tblQualifiedName = Assembly.CreateQualifiedName(appAssm.FullName, tblTypeFullName)
      tblType = Type.[GetType](tblQualifiedName)
      If tblType Is Nothing Then
        Throw New Exception(String.Format("Cannot find class {0}Table or {0}View.", tblName))
      End If
    End If
 
    ' Retrieve the record
    Dim tblInstance As PrimaryKeyTable = TryCast(tblType.GetField("Instance").GetValue
    (Nothing), PrimaryKeyTable)
    Dim rcd As PrimaryKeyRecord = TryCast(tblInstance.GetRecordData(rcdId, False),
    PrimaryKeyRecord)
 
    ' Create a blank page and load the record panel user control.
    Dim pg As Page = New BaseApplicationPage()
    pg.EnableViewState = False
    Dim panel As Control = pg.LoadControl(pnlPath)
    Dim rcdCtrl As Control = MiscUtils.FindControlRecursively(panel, String.Format("_{0}
    RecordControl", tblName))
 
    ' Bind data to the panel
    rcdCtrl.[GetType]().GetProperty("DataSource").SetValue(rcdCtrl, rcd, Nothing)
    rcdCtrl.DataBind()
 
    ' Render the page
    Dim tempForm As New HtmlForm()
    tempForm.Controls.Add(panel)
    pg.Controls.Add(tempForm)
    Dim sw As New StringWriter()
    Try
      HttpContext.Current.Server.Execute(pg, sw, False)
    Catch ex As Exception
      Dim msg As String = ex.Message
    End Try
 
    ' Strip out the panel html
    Dim outputToReturn As String = sw.ToString()
    outputToReturn = outputToReturn.Substring(outputToReturn.IndexOf("<div>"))
    outputToReturn = outputToReturn.Substring(0, outputToReturn.IndexOf("</form>"))
    Return outputToReturn
  End Function
 
End Class

Step 2: Build a ShowRecord ascx UserControl
Create a new ShowRecord panel and save it as an .ascx UserControl. Configure and customize it as usual. Turn off SmoothPanelUpdate on the record panel to ensure the web service executes successfully. Alternatively, turn the panel heading off and use the popup frame in its place.

Step 3: Register the web service on calling page
In the calling page’s codebehind file (xxx.aspx.cs or xxx.aspx.vb), we register the web service with the following code.

C#
public ApplicationList() {
  this.Initialize();
  Init += new EventHandler(ApplicationList_Init);
}
 
void ApplicationList_Init(object sender, EventArgs e) {
  scriptManager1.Services.Add(new ServiceReference("~/RecordPanelService.asmx"));
}

Visual Basic .NET
Public Sub New()
  Me.Initialize()
  AddHandler Init, AddressOf ApplicationList_Init
End Sub
 
Private Sub ApplicationList_Init(ByVal sender As Object, ByVal e As EventArgs)
  scriptManager1.Services.Add(New ServiceReference("~/RecordPanelService.asmx"))
End Sub

Step 4: Add JavaScript code to display popup on calling page
To display the web service’s return result (record panel HTML content) define a javascript function in the xxx.html (.aspx) file. This calls the detailRolloverPopup() predefined in ApplicationWebForm.js. If the calling page requires several different record panels with similar sizes, we can define a single function to serve all panels. Or, depending on the scenario, we might want to define separate functions for different panels.

JavaScript
<script type="text/javascript">
function popupApplicantDetail(result){
  detailRolloverPopup("Applicant Detail", result, true, 350, 150, true);
}
</script>

Step 5: Customize the trigger control to call the web service
On the calling page, we place controls such as LinkButton or ImageButton to call the web service. The following code adds the trigger to an ImageButton’s OnClick event. The code is in the xxx.Controls.cs (xxx.Controls.vb) file.

C#
public class ApplicationsTableControlRow : BaseApplicationsTableControlRow {
 
  public override void DataBind() {
    base.DataBind();
 
    if (DataSource != null) {
      ApplicationsRowViewButton.Attributes["OnClick"] = string.Format(
        "SaveMousePosition(event); RecordPanelService.GetRecordPanel('{0}', '{1}', '{2}',
        popupApplicantDetail); return false;",
        "Applications", "~/Applications/ApplicantDetail.ascx", DataSource.ID0.ToString());
    }
  }
}

Visual Basic .NET
Public Class ApplicationsTableControlRow
  Inherits BaseApplicationsTableControlRow
 
  Public Overloads Overrides Sub DataBind()
    MyBase.DataBind()
 
    If DataSource IsNot Nothing Then
      ApplicationsRowViewButton.Attributes("OnClick") = String.Format("SaveMousePosition(event)
      ; RecordPanelService.GetRecordPanel('{0}', '{1}', '{2}', popupApplicantDetail); return
    false;", "Applications", "~/Applications/ApplicantDetail.ascx", DataSource.ID0.ToString())
    End If
  End Sub
End Class

Conclusion

Displaying a record panel in the AJAX popup combines two excellent Iron Speed Designer features and is an attractive alternative to redirect to a separate ShowRecord page. This is especially true where the record panel content is more appropriately displayed as a popup.

Download

Popup Demo project (Iron Speed Designer v6.1.0)

About the Author

Herman Chan, PMP, MCAD.NET, J2CP, is the founder of Presence Consulting Group. With over 12 years of experience in Information Technology, he has assumed different technical roles which include project manager, development lead, independent technical consultant, developer, etc. He is passionate about technology and always believes delivering good user experience is one of the most important aspects of software development. Currently, he is leading various technical projects with a consulting, service-oriented approach.

Presence Consulting Group is an established technical consulting firm servicing a wide range of clients, ranging from land management company to direct mail marketers. Our services include .NET consulting, Iron Speed Designer training, database modeling, creative website design, and project management.



  Privacy Statement