Welcome back to our series on getting started with the ASP.Net AJAX Controls.  In this series, I’m demonstrating the creation of a simple web application called “Room Scheduler” that allows a fictional hotel to manage guests and their reservations.  Our previous articles in this series demonstrated the following topics:

  • Project Introduction and Navigation – We created a Telerik RadControls Web Application, added a Web Site Layout in a MasterPage, and configured navigation for the application with a RadMenu control.
  • Gauging the Data Configuration – We defined the database configuration with Entity Framework 5 and Sql Server Compact and added some sample data.  We also verified data was loading properly by adding a RadGauge to the start of our application dashboard.

AJAX dashboard for the hotel staff

In this article, I am going to show you how to complete a dashboard for the hotel staff.  With this simple dashboard, a user can easily see the current guest list and review any of those guests complete reservation details with a single click.  You can follow along by downloading the source code from the previous article in the series.  You will also need to have Visual Studio 2012 and the Telerik AJAX controls installed.  A trial copy of the Ajax controls is available here.

Adding a List of the Current Guests

For this dashboard, we are adding a current guest list underneath of the gauge control added in the prior article.  This allows our users to see who is currently staying in the hotel and select one to review in the right side of the dashboard.  To ensure the user sees a graceful animation when the dashboard content is loading, I’m adding a RadAjaxLoadingPanel as well.  My left panel now looks similar to the following:

<div class="leftPanel">
    <%-- Snipped the RadRadialGauge for brevity --%>
 
    <%-- Show the cool animations when we reference the server --%>
    <telerik:RadAjaxLoadingPanel ID="loadingPanel" runat="server" />
 
    <%-- Add Grid Here  --%>
 
</div>
Code Listing 1 - Current Guests Grid Placement

To present this collection of guests I have chosen to use one of the most useful controls – Telerik's ASP.Net AJAX Grid, called the RadGrid.  In this scenario, I want to add the grid so that it lists a guest’s name along with the room number that they are occupying.  Additionally, I would like users to be able to sort the list of guests.  Finally, the user should be able to click anywhere in the row to select the guest they would like to know more about.

To deliver these requirements, I have added a RadGrid where indicated with the comment in Code Listing 1.  I configured most of this functionality through the smart tag that displays after I add the control to the page.  For this grid, I marked the checkboxes in the smart tag to enable sorting, scrolling, and client-side row selection:

Smart Tag for the RadGrid
Figure 1 - Smart Tag for the RadGrid


I am choosing to access the data source for this grid in the code-behind.  In the RadGrid, there is an event called NeedDataSource the grid control calls when performing an action that requires data.  I will attach my data handling code to this event.  

protected void currentGuestsGrid_NeedDataSource(object sender,
     GridNeedDataSourceEventArgs e)
{
     currentGuestsGrid.DataSource = GetTodaysGuests();
}
 
private void GetTodaysGuests()
{
    var beginDay = DateTime.Today;
    var endDay = DateTime.Today.AddDays(1).AddSeconds(-1);
    var todaysRes = _ThisContext.Reservations
        .Where(r => r.ArrivalDate < endDay && r.DepartureDate >= beginDay)
        .Select(r => new {
            ResId = r.ReservationID,
            RoomNum = r.Room.Address,
            GuestName = r.Guest.FirstName + " " + r.Guest.LastName
    });
 
    return todaysRes.ToList();
}

 

Code Listing 2 – The Current Guests data access code

There are some interesting things to notice about this block of code.  First, and this is my preference, I do not perform data access directly in the event handler.  I separate this into a different method to allow for re-use and refactoring later.  Second, the NeedDataSource event does not actually call DataBind on the currentGuestsGrid.  This is because the grid handles applying any sorting and filtering behind the scenes after the event-handler executes, saving you from having to identify and create filtering and sorting modifiers.

 

Finally, to complete the configuration of this grid, I want to define a ke3y for each row.  In my data returned, I returned the key for this dataset in the ResId property.  To configure the RadGrid to be aware of this unique identifier, I will need to add a MasterTableView declaration in the source view with ClientDataKeyNames property configured.  Don't confuse this with the DataKeyNames property that will deliver to your server-side code the same values.  The results of this configuration, including some style adjustments to the columns appears below:

<telerik:RadGrid ID="currentGuestsGrid" runat="server" AutoGenerateColumns="False"
    CellSpacing="0" GridLines="None" AllowSorting="True"
    OnNeedDataSource="currentGuestsGrid_NeedDataSource">
 
    <ClientSettings EnableRowHoverStyle="true">
        <ClientEvents OnRowClick="currentGuestsGrid_RowClick" />
        <Selecting EnableDragToSelectRows="False" AllowRowSelect="true" />
        <Scrolling AllowScroll="True" UseStaticHeaders="True" />
    </ClientSettings>
 
    <MasterTableView DataKeyNames="ResId" ClientDataKeyNames="ResId">
 
        <Columns>
            <telerik:GridBoundColumn DataField="RoomNum" HeaderText="Room" UniqueName="room">
                <FooterStyle Width="100px" />
                <HeaderStyle Width="100px" />
                <ItemStyle Width="100px" />
            </telerik:GridBoundColumn>
            <telerik:GridBoundColumn DataField="GuestName" HeaderText="Guest" UniqueName="column">
            </telerik:GridBoundColumn>
        </Columns>
 
    </MasterTableView>
 
</telerik:RadGrid>
Code Listing 3 - Current Guests RadGrid Definition

Adding the Reservation Details Panel Layout


On the right side of the screen, we want to show the details of the selected reservation.   I’m not going to focus on how the data will be loaded into these fields yet, I just want to get a layout on the screen and will wire-up the data sources later.  To format this layout, I have added an asp:Panel with a simple collection of fields:

<asp:Panel ID="reservationDetailsPanel" runat="server" CssClass="detailsPanel" Visible="true" ClientIDMode="Static" >
 
<asp:Label runat="server" ID="lNoReservationSelected" CssClass="NoReservationSelected" Text="No Reservation Currently Selected"></asp:Label>
 
<asp:Literal runat="server" ID="lReservationDetails"><h3>Reservation Details</h3></asp:Literal>
 
<asp:Literal runat="server" ID="lDetails"><h4>Guest Details:</h4></asp:Literal>
 
<table>
    <tr>
        <td>
            <asp:Label runat="server" ID="lblLastName" Width="150" AssociatedControlID="txtLastName">Last Name:</asp:Label>
        </td>
        <td>
            <telerik:RadTextBox runat="server" ID="RadTextBox1" Width="150" Enabled="false"></telerik:RadTextBox>
        </td>
        <td>
            <asp:Label runat="server" ID="lblFirstName" Width="150" AssociatedControlID="txtFirstName">First Name:</asp:Label>
        </td>
        <td>
            <telerik:RadTextBox runat="server" ID="RadTextBox2" Width="150" Enabled="false"></telerik:RadTextBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:Label runat="server" ID="lblAssignedRoom" AssociatedControlID="assignedRoom">Assigned Room:</asp:Label>
        </td>
        <td colspan="3">
            <asp:Label runat="server" ID="assignedRoom"></asp:Label>
        </td>
    </tr>
</table>
 
<div id="chargesHeader">
    <asp:Literal ID="lCharges" runat="server"><h4>Charges:</h4> </asp:Literal>
    <asp:Literal ID="lTotalCharges" runat="server"><h4>Total: </h4></asp:Literal>
</div>
<br />
<%-- Grid of Charges goes here --%>
 
<br />
<asp:Literal ID="lNotes" runat="server"><h4>Notes:</h4></asp:Literal>
<%-- Editor for Notes goes here --%>
 
</asp:Panel>
Code Listing 4 - The Reservation Details Panel Layout

The content of this panel is a collection of standard ASP.Net controls and layout with a few RadTextBoxes thrown in to make things interesting.  The more interesting parts start with the grid of room charges we will place in the middle of the panel.  I will place a standard RadGrid here and configure two columns, one for the charge description and one for the value associated with the charge.  With the minimal amount of configuration that I am doing for this grid, I will write this code by hand in the source editor:

<telerik:RadGrid ID="chargesGrid" runat="server" Width="500px" Height="200px" Visible="false" AutoGenerateColumns="False" CellSpacing="0" GridLines="None">
 
    <MasterTableView>
        <Columns>
            <telerik:GridBoundColumn DataField="Description" UniqueName="description" HeaderText="Description"></telerik:GridBoundColumn>
            <telerik:GridBoundColumn DataField="Value" UniqueName="value" HeaderText="Charged" DataFormatString="{0:$0.00}">
                <ItemStyle HorizontalAlign="Right" />
            </telerik:GridBoundColumn>
        </Columns>
 
    </MasterTableView>
 
</telerik:RadGrid>
Code Listing 5 - The Room Charges Grid

In Code Listing 5, notice how I formatted the Value column with a datafield called Value and a HeaderText of “Charged”.  You are never bound to use the name of the data field as your column header, a helpful concept when you need to localize your webpages.  Additionally, I have added a DataFormatString to this column to make it appear in American currency.

To complete this panel’s layout, I will add an AccessibleRadEditor to the bottom as indicated in Code Listing 6.  I don’t need to configure anything special about this control, as the default configuration meets my immediate needs.

<br />
<asp:Literal ID="lNotes" runat="server"><h4>Notes:</h4></asp:Literal>
<telerik:AccessibleRadEditor runat="server" ID="notesEditor" Height="200" Width="500" />
 
</asp:Panel>
Code Listing 6 - The Notes Editor in place

Making the Magic – Loading Data on an AJAX click event


Finally, to load the data into the reservation details panel we need to connect a client-side event to a series of server side methods that will fetch and paint the data in the appropriate controls.  The first method I wrote is a short show/hide method to display the “No Reservation Selected” or the controls in the reservation details grid:

private void ToggleReservationDetailsVisibility(bool displayControls)
{
    var controlsToHide = reservationDetailsPanel.Controls;
    foreach (Control ctl in controlsToHide)
    {
        ctl.Visible = (displayControls) ? (ctl.ID != "lNoReservationSelected") : (ctl.ID == "lNoReservationSelected");
    }
 
}
Code Listing 7 - Show/Hide the controls in the Reservation Details Panel

I have placed an initial call to this method in an override of the CreateChildControls method of this WebForm.  Also in this method, I shall connect the AjaxManager to our Reservation Details Panel for AJAX updates:

protected override void CreateChildControls()
{
    base.CreateChildControls();
 
    ToggleReservationDetailsVisibility(false);
 
    var ajaxMgr = RadAjaxManager.GetCurrent(this);
    ajaxMgr.AjaxRequest += ajaxMgr_AjaxRequest;
 
    ajaxMgr.AjaxSettings.AddAjaxSetting(ajaxMgr, reservationDetailsPanel);
 
}
Code Listing 8 - Create Child Controls method – connecting events to the AjaxManager


Please also note the declaration for the AjaxRequest event handler.  This connection tells the AjaxManager what method triggers when an AjaxRequest is submitted.  The final line of this listing is the glue that tells the AjaxManager to update the HTML in the reservation details panel when the AjaxManager triggers the AjaxRequest.

In order for the current guests grid to trigger the AjaxManager to send an AjaxRequest, I need to specify some client-side script for the ClientEvent “OnRowClick”.  The RadGrid defines the client-side event for handling a row-click with this short snippet of code in the ClientEvents tag of the currentGuestsGrid:

<ClientSettings EnableRowHoverStyle="true">
    <ClientEvents OnRowClick="currentGuestsGrid_RowClick" />
    <Selecting EnableDragToSelectRows="False" AllowRowSelect="true" />
    <Scrolling AllowScroll="True" UseStaticHeaders="True" />
</ClientSettings>
Code Listing 9 - ClientEvents configuration to trigger JavaScript execution


I will now add a small block of JavaScript to the bottom of the page to trigger the AjaxRequest in the AjaxManager when the OnRowClick event is fired:

function currentGuestsGrid_RowClick(sender, args) {
             
    var key = args.getDataKeyValue("ResId");
    $find("<%= RadAjaxManager.GetCurrent(this).ClientID %>").ajaxRequest(key);
 
}
Code Listing 10 - JavaScript to execute an AjaxRequest on the AjaxManager

Several things are happening here, let’s discuss them briefly.  First, we fetch the value stored in the DataKey (remember we configured this in Code Listing 3).  Next we identify the client-side implementation of the AjaxManager with the $find() method and trigger the AjaxRequest method.  By submitting this key as the argument to that method, we will have the value available in the server-side code.  

void ajaxMgr_AjaxRequest(object sender, AjaxRequestEventArgs e)
{
 
    int resId = Convert.ToInt32(e.Argument);
    this.DisplayReservationDetails(resId);
    ToggleReservationDetailsVisibility(true);
 
}
Code Listing 11 – Server-side handling of the AjaxRequest

Here we can see that the ResId field captured in our JavaScript is passed in the AjaxRequestEventArgs.Argument property.  I converted that value back into an integer so that I can use it to query the database for additional reservation data.  I then make a call to the ToggleReservationDetailsVisibility method to show the newly populated fields on the screen.  The content of the DisplayReservationDetails method is available in the code download associated with this article.

There you have it… a functional dashboard that allows you to click on a guest on the left side and populate their reservation details on the right side.  Below is a screenshot of how the final product looks:

Final Layout of the Room Scheduler Dashboard
Figure 2 – Results of our Ajax coding efforts, an interactive set of dashboard panels

Conclusion


We went through a lot of code in this article.  By adding a handful of controls to our designer surface and connecting a pair of data sources and some JavaScript, we now have a compelling interface for our users.  They can easily see a list of current guests and select a guest to review their reservation details without observing a complete screen refresh.

You can download the code for our sample project here.  Please leave any questions or comments in the section below.



About the Author

Jeffrey T. Fritz

Jeffrey T. Fritz is a Microsoft MVP in ASP.Net and an ASPInsider with more than a decade of experience writing and delivering large scale multi-tenant web applications. After building applications with ASP, ASP.NET and now ASP.NET MVC, he is crazy about building web sites for all sizes on any device. You can read more from Jeffrey on his personal blog or on Twitter at @csharpfritz. Google Profile


Related Posts

Comments