Dealing with complex Eval() expressions inside of ItemTemplate or other data containers always makes me do a double take. Here are a few observations and thoughts on how to handle and possibly improve handling of this feature in the future.
So, a typical scenario is embedding controls inside of an item template. The template loops through the datasource and you now want to embed values from the datasource into the child controls of the ItemTemplate.
For example:
<asp:repeater id="rptSpecials" runat="server">
<itemtemplate>
<asp:HyperLink runat="server"
NavigateUrl='~/item.aspx?sku=<%# Eval("sku") %>'
Text='<%# Eval("specialhd") %>'/>
</itemtemplate>
</asp:repeater>
Quick what does that give you?
Not what you might expect:
<a href="item.aspx?sku=<%# Eval("sku") %>">West Wind Html Html Help Builder 4.02</a>
It's slightly inconsistent isn't it? Text expands just fine with the Eval expression, but the NavigateUrl() doesn't.
Now can anybody spot why this is happening?
If you change the NavigateUrl expression to:
NavigateUrl='item.aspx?sku=<%# Eval("sku") %>'
it turns out it actually works. The problem is that that ~/ is causing ASP.NET to call ResolveUrl() which takes the whole string and mucks up the string. Take the ~ out and ASP.NET no longer calls ResolveUrl and it actually works.
Now it seems to get this to work anyway is this:
<asp:HyperLink runat="server"
NavigateUrl='<%# Request.ApplicationPath %>/item.aspx?sku=<%# Eval("sku") %>'
Text='<%# Eval("specialhd") %>'/>
But that results in the following error:
Compiler Error Message: CS1040: Preprocessor directives must appear as the first non-whitespace character on a line
Source Error:
|
Line 7: <tr>
Line 8: <td valign="top">
Line 9: <asp:HyperLink runat="server"
Line 10: NavigateUrl='<%# Request.ApplicationPath %>/item.aspx?sku=<%# Eval("sku") %>'
Line 11: Text='<%# Eval("specialhd") %>'/> |
Source File: c:\projects2005\wwstore\UserControls\SpecialsListing.ascx Line: 9
If you add a character (say a<%# …%> then it works). I have no idea why this would be a problem for ASP.NET to parse, all I know is it doesn't work.
I suspect there's a bug in the ASP.NET parser in this situation, especially for the first case where apparently ASP.NET is calling ResolveUrl before it's doing the eval on the embedded string.
With all this sort of headache ultimately it's easier to just write out the HREF manually:
<a href="<%# Request.ApplicationPath %>"/item.aspx?sku=<%# Eval("sku") %>">
<img src="<%# Request.ApplicationPath %>/itemimages/sm_<%# Eval("Itemimage") %>" />
</a>
I bring this up because this sort of thing seems to happen quite frequently. I really wish there was better support for assigning dynamic values. Maybe something using FormatStrings that could be replaced with some dynamic properties:
NavigateUrl="~/MyPage/MyPage?Id={0}" Text="SomeText {1}"
Parameter0="<%# Eval("Sku") %>
Parameter1="<%# Eval("Company") %>"
It may seem like this is overkill but I find myself running into situations quite frequently where the single string delimiter makes it near impossible to create a clean expression in script code. The above solution would solve that problem in all situations because you'd eliminate the need to nest the Eval expression with its string delimiter and you get back the use of two string delimiters.
There are workarounds today, they're just not quite as declarative. The above behavior could be simulated with something like this:
NavigateUrl='<%# this.ResolveUrl("~/" + string.Format("item.aspx?sku={0}",Eval("sku")) %>'
Still that doesn't really get around the string delimiter issue. Try this:
NavigateUrl='<%# this.ResolveUrl("~/" + string.Format("onclick=DoItem('{0}');",Eval("sku"))%>'
There's the problem with the string nesting. Remove the Eval() completely from this expression would fix this problem.
Today my workaround for complex expressions that just don't want to work is to create a method on the page or control that returns the value which is workable, but not a solution the average novice will think of.
<asp:HyperLink runat="server"
NavigateUrl='<%# this.GetItemUrl( Eval("sku") as string ) %>'
Text='<%# Eval("specialhd") %>'/>
where GetItemUrl() simply does all of the formatting for the URL.