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:
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
>
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: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();
}
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
>
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
>
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
>
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
>
private
void
ToggleReservationDetailsVisibility(
bool
displayControls)
{
var controlsToHide = reservationDetailsPanel.Controls;
foreach
(Control ctl
in
controlsToHide)
{
ctl.Visible = (displayControls) ? (ctl.ID !=
"lNoReservationSelected"
) : (ctl.ID ==
"lNoReservationSelected"
);
}
}
protected
override
void
CreateChildControls()
{
base
.CreateChildControls();
ToggleReservationDetailsVisibility(
false
);
var ajaxMgr = RadAjaxManager.GetCurrent(
this
);
ajaxMgr.AjaxRequest += ajaxMgr_AjaxRequest;
ajaxMgr.AjaxSettings.AddAjaxSetting(ajaxMgr, reservationDetailsPanel);
}
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
>
function
currentGuestsGrid_RowClick(sender, args) {
var
key = args.getDataKeyValue(
"ResId"
);
$find(
"<%= RadAjaxManager.GetCurrent(this).ClientID %>"
).ajaxRequest(key);
}
void
ajaxMgr_AjaxRequest(
object
sender, AjaxRequestEventArgs e)
{
int
resId = Convert.ToInt32(e.Argument);
this
.DisplayReservationDetails(resId);
ToggleReservationDetailsVisibility(
true
);
}
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:
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.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.