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.