Элементы шаблонов в ASP.NET WebForms, нетривиальный data binding

01.11.2009 12:35 / Артём Волк / 4296 просмотров / ...

WebForms задумывался для создания кода, обрабатывающего события пользовательского интерфейса, однако в некоторых случаях бывает необходимо отойти от этой парадигмы и использовать кодовые вставки <%...%> в .aspx-файлах почти в стиле ASP.NET MVC.

Встроенные компоненты, использущие т.н. data binding тоже иногда заставляют задуматься :)

Регистрация namespace'ов

Для того, чтобы сократить запись кода в .aspx-файлах бывает удобно импортировать неймспейсы (аналог using для code-behind). Сделать это можно на уровне страницы:

<%@ Import Namespace="CrispStudio.Routine" %>

Либо для всех страниц сайта в Web.config:

<pages>
        <controls>
			<!-- А здесь можно зарегистрировать сторонние контролы, не добавляя их в Toolbox -->
        </controls>
		<namespaces>
			<add namespace="CrispStudio.Routine"/>
			...
		</namespaces>
 </pages>

Назначение свойств внутри data binding controls

<asp:LinkButton ID="lbDelete" runat="server" 
		OnClientClick="return confirm('Вы уверены, что хотите удалить этот раздел?');" 
		CommandName="remove" 
		CommandArgument='<%# ((Folder)Container.DataItem).NFolder %>'>
			<img src="Images/Admin/delete.png" width="16" height="16" alt="Удалить" title="Удалить" />
</asp:LinkButton>

Вложенные контролы

В данном случае это GridView, завёрнутый в web user control внутри Repeater'a

<asp:Repeater ID="rSummary" runat="server" EnableViewState="False" OnItemDataBound="rSummary_ItemDataBound">
	<ItemTemplate>
		...
		<uc2:GridControl ID="GridControlInner" runat="server" />
		...
	</ItemTemplate>


protected void rSummary_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
	RepeaterItem item = e.Item;
	if ((item.ItemType == ListItemType.Item) ||
		(item.ItemType == ListItemType.AlternatingItem))
	{
		Web_Controls_GridControl grid = (Web_Controls_GridControl)item.FindControl("GridControlInner");
		grid.DataSource = ...;
	}
}

Биндинг коллекции объектов

В данном случае коллекция объектов типа TextPage:

<asp:Repeater ID="rTextPage" runat="server">
	<ItemTemplate>
		<h1><%# ((TextPage)Container.DataItem).TextPageTitle %>            
		<asp:PlaceHolder ID="cphEdit" runat="server" Visible="<%#WebUser.IsContentAdmin %>">            
			<!-- Ссылка на админку, которая видна для соответствующих пользователей -->
		</asp:PlaceHolder>                
		</h1>            
            <%# ((TextPage)Container.DataItem).TextPageText %>
    </ItemTemplate>
</asp:Repeater>

В этом же примере один из вариантов управления показом части контента по условию.

Использование Repeater'a для одного элемента

Этот удобный контрол можно использовать как простой шаблонизатор и в том случае, когда элемент всего один:

rTextPage.DataSource = new TextPage[] {currentPage};
rTextPage.DataBind();

Простая проверка по условию (спасибо ASP.NET MVC :))

<% if (HideLogoLink) { %>
	<img src="images/logo.jpg" width="196" height="126" alt="Логотип"/>
<% } else { %>
		<a href="..."><img src="images/logo.jpg" width="196" height="126" alt="Логотип"/></a>					
<% } %>

Использование тернарного оператора (hardcore-версия)

<img src="<%# 
	( ((Good)Container.DataItem).GoodImageType != null && ((Good)Container.DataItem).GoodImageType != String.Empty) ?
		String.Format(Resources.General.ImageGoodsUrlThumb, Resources.General.UploadFolder, Resources.General.ImageTypeGoods, 		((Good)Container.DataItem).NGood, ((Good)Container.DataItem).GoodImageType)
	:	
		"Images/good_no_image.jpg"
	%>" alt="<%# ((GoodCustom)Container.DataItem).Producer.ProducerName%>&nbsp;<%# ((GoodCustom)Container.DataItem).GoodName %>" />

Определение функций в .aspx-файлах

Иногда удобно определить функцию прямо в шаблоне (например, если она будет использоваться один раз), главное — не увлекаться:

<script runat=server>
	protected string doSomeStuff(object tempObj)
	{
		return tempObj.ToString();
	}
</script>								

<asp:Repeater ID="rFirstLevel" runat="server" EnableViewState="false">
	<ItemTemplate>
		<%# doSomeStuff(Container.DataItem) %>	
	</ItemTemplate>
</asp:Repeater>

Номер итерации в data binding conrol'e

<%#Container.ItemIndex %>

HTML в заголовке колонки GridView при использовании ASP.NET CSS Adapters

<asp:TemplateField HeaderText='<div style="text-align: right">Цена</div>'>

Биднинг Dictionary

<%# Eval("key") %> 
<%# Eval("value") %>

Проверка наличия данных в Repeater

Вариант 1:

<% noResuts.Visible = (rResults.Items.Count > 0); %>
<asp:PlaceHolder ID="noResuts" runat="server">		
	Нет результатов       
</asp:PlaceHolder>
<asp:Repeater ID="rResults" runat="server">
	<ItemTemplate>
		...
	</ItemTemplate>
<asp:Repeater>

Вариант 2:

	
	<% if (rResults.Items.Count == 0) { %>
		Нет результатов
	<% } %>
	<asp:Repeater ID="rResults" runat="server">
		<ItemTemplate>
			...
		</ItemTemplate>
	<asp:Repeater>

Использование <%=..%> внутри data binding контролов

Напрямую блоки кода, отличные от <%#..%> вставлять внутрь data binding контролов не разрешается. Решение простое, нужно вставить внутрь data binding контрола контрол PlaceHolder.