Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Dynamic Templates for the Repeater, DataList and DataGrid Controls

This article explains why a single template hard-coded at design-time is not always the best option when using DataGrid, DataList and Repeater controls that must generate different outputs according to the logged-in user, a user's choice, or any other condition. It then provides detailed solutions to implement dynamic templates, by writing external user controls, or classes that implement the ITemplate interface.


advertisement
ost of the time, when you add a Repeater, DataList or DataGrid control on the page, you hardcode their templates: you know the data you want to show, and to do this you declare Literal, Label, CheckBox and any other type of control with bindable properties, or simply add static HTML and binding expressions.

This is fine as long as you have to render only one view of the data, but what happens when you want to use the same template-based control (i.e. one of the controls mentioned above) for showing different data, according to the currently logged-in user? Suppose for example that you have to build a report page for the Northwind’s Employees table, and want to show the complete employee’s information (name, full mailing address, salary etc.) if the current user is an Administrator, or just the employee’s name and title otherwise, so to protect their privacy from non-authorized people. Another example: you may want to provide Edit and Delete buttons to each row when the user is an Administrator, but of course not for the normal user.

Yet another example: it may be the users themselves to decide whether they want to see some data in the report. Maybe the user (even if an administrator) doesn’t want to see the employees’ sensitive information, because she wants to print the report and distribute it among other people not authorized to see that data, or simply because she wants to keep the report as short and clean as possible, so it’s easier to read.



A possible solution is to bind the Visible property of the control declared inside the template to a function that returns True or False according to the current user’s roles, to the state of a checkbox/option button on the page, or to any other custom rule. For example, here’s how to make a delete button and the employee’s address visible to administrators only (users of the Windows’ BUILTIN\Administrators group):

<asp:DataList Runat="server" ...> <ItemTemplate> <asp:Button runat="server" Text="Edit" CommandName="Edit" Visible='<%# ShowAdminControls() %>'/> <%# Container.DataItem("TitleOfCourtesy") %> <%# Container.DataItem("LastName") %>, <%# Container.DataItem("LastName") %> <asp:Label runat="Server" Text='<%# <br> & Container.DataItem("LastName") %>' Visible='<%# ShowAdminControls() %>'/> </ItemTemplate> </asp:DataList> in the code-behind file, or the server-side script section Function ShowAdminControls() As Boolean Return User.Identity.IsAuthenticated AndAlso _ User.IsInRole("BUILTIN\Administrators") End Function

Although using many binding expressions can slow down the page’s execution makes the code in the ASPX file more difficult to read, this solution can be ok if you have to handle just a couple of possible report’s view and you don’t have to dynamically hide/show many controls, but just a couple of buttons or labels, as in the last example.

However, if you need to render different outputs for more than two groups of users, and also according their options, this solution will soon become an hell to manage! You’ll end up having a single huge template section with lots of controls and lots of binding expressions to decide what must be shown and what must be hiddennot an ideal solution, also with regards to maintainability and readability. And don’t forget the worse performances: the binding expressions will be evaluated for every single data source’s item bound to the control, and this can be thousands of times if you have a few hundred records but dozens of controls that must be visible according to dynamic rules!

A much better solution does fortunately exist, and it is probably simpler than what you may thing, especially considering the technique explained above.

Dynamically loading templates from external files

If you need different templates for different users and options, you can easily define the content for these templates in multiple separate file, and then load then dynamically at runtime, according to your own business rules! What you put in these files is almost the same you’d put into the control’s xxxTemplate section (it works not only for the ItemTemplate, but also for the HeaderTemplate etc.), what changes is only the way you refer to the row’s data item. First, this is how to declare the DataList control that will be used for the template files we’ll see shortly:

<asp:DataList ID=Datalist1 runat="server" Width="100%" AlternatingItemStyle-ForeColor="Maroon" AlternatingItemStyle-BackColor="LightYellow" ItemStyle-ForeColor="DarkBlue" ItemStyle-BackColor="LightCyan" ...other style formatting...> <HeaderTemplate>Employees</HeaderTemplate> <ItemTemplate></ItemTemplate> <FooterTemplate></FooterTemplate> </asp:DataList>

As you see, the only particular thing is that the ItemTemplate section is completely empty. In reality, we could even avoid to declare it, but without it the Visual Studio .NET’s visual designer would not be able to render the control, and would show a gray box instead, as it does when adding user control. At this point we have to write the template files. In the first template file, DataList_SimpleTemplate.ascx, we paste some of the code of the original DataList's ItemTemplate template: the three simple binding expressions that render the employee’s title of courtesy, last and first names, as shown in the first example. We should add a @Control directive, as this is actually a user control file. We must also cast the Container object to DataListItem, because the template is defined outside the DataList, and thus the Container reference is of a general object type without the required DataItem property. After making these changes, you will have the following code:

<%@ Control %> <%# DataBinder.Eval( _ CType(Container, DataListItem).DataItem, "TitleOfCourtesy") %> <b> <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "LastName") %> </b>, <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "FirstName") %>

The code above uses Visual Basic .NET. If we were using C# we should specify this in the @Control directive, as we would do with a page. The C# code would be as follows:

<%@ Control Language="C#" %> <%# DataBinder.Eval( ((DataListItem)Container).DataItem, "TitleOfCourtesy") %> <b> <%# DataBinder.Eval(((DataListItem)Container).DataItem, "LastName") %> </b>, <%# DataBinder.Eval(((DataListItem)Container).DataItem, "FirstName") %>

It looks similar, but of course there is a different syntax for type casting. The second template (we’ll only see the VB.NET from now on), saved in DataList_TemplateEx.ascx, is similar: it merely adds some data, namely the content of the Address, City, and Region fields of the current record. Here's its complete code:

<%@ Control %> <font face="Verdana"> <%# DataBinder.Eval( _ CType(Container, DataListItem).DataItem, "TitleOfCourtesy") %> <b> <%#DataBinder.Eval(CType(Container, DataListItem).DataItem, "LastName") %> </b>, <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "FirstName") %> <br> <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "Title") %> <br> <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "Address") %> - <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "City") %>, <%# DataBinder.Eval(CType(Container, DataListItem).DataItem, "Region") %> </font>

Once the template files are ready, we can load them when the page loads, according to some rules. In this example, we’ll load and associate to the DataList the DataList_TemplateEx.ascx template if the current user is in the Window’s Administrators group, and the other template otherwise. As you should already know, to being asked a username and password and using Windows security you must first disable the anonymous access and enable the Basic Authentication options in the IIS virtual directory’s Properties page. Once we have the virtual path of the template file, we load it by calling the Page's LoadTemplate method, and assign the returned object to the DataList's ItemTemplate property. The method below shows all this:

Private Sub Page_Load() Handles MyBase.Load ' load the proper templates according to the logged-in user If User.IsInRole("BUILTIN\Administrators") Then Datalist1.ItemTemplate = Page.LoadTemplate("DataList_TemplateEx.ascx") Else Datalist1.ItemTemplate = Page.LoadTemplate( _ "DataList_TemplateSimple.ascx") End If BindControls() End If

This is all you need for a basic use of external template files, and the figure below shows how the two datalists look like in admin and normal user mode, respectively:

Suppose that you want to show different data for five different groups of users, and you’ll immediately understand the power and ease of use of this solution, in comparison with hardcoding within the same template all the controls and binding expressions for showing all the data, and then showing/hiding them in various combinations to produce the wanted output for the current user.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date