ОБСУЖДЕНИЕ

Расширение функциональности Web контроллеров. Часть 1

6 ответов 7 тыс.
AI-выжимка обсуждения скоро

Краткие тезисы обсуждения со ссылками на ключевые ответы появятся здесь.

Автор темы
Пусть мы имеем DataGrid Web контроллер на aspx странице, как показано на рисунке.

DataGrid.jpg

Каждая строка содержит Auto CheckBox, Price TextBox Web контроллеры. Если Auto установлен, то ввод информации в Price запрешен, иначе разрешен. Эта задача решается легко на сервере с помощью события OnCheckedChanged. Но при этом aspx страница перезагружается. Чтобы решить эту проблему на клиенте добавим Java script к Auto CheckBox Web контроллеру.
Создадим новый проект в Visual Studio 2005 типа Class Library с именем CheckBoxNik. Добавим новый тип CheckBoxNik.CheckBox, который наследует из System.Web.UI.WebCotrols.CheckBox, как видно из следующего кода.

Код:
using System;

namespace CheckBoxNik
{
public class CheckBox: System.Web.UI.WebControls.CheckBox
{
string _targetControlID;
const string _addAttribute = "onClick(this, '{0}');";


#region Properties
public string TargetControlID
{
get
{
return _targetControlID;
}
set
{
_targetControlID = value;	
}
}
#endregion

#region Members overrides
protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
{

writer.AddAttribute("onClick", String.Format(_addAttribute, TargetControlID));

base.AddAttributesToRender(writer);
}

protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterClientScriptInclude("CheckBoxNik", Page.ClientScript.GetWebResourceUrl(this.GetType(), "CheckBoxNik.JavaScript.js"));

base.OnPreRender(e);
}
#endregion

}
}

Тип имеет приватное поле – _targetControlID для хранения идентификатора Price контроллера, доступ к которому обеспичивает свойство TargetControlID. Кроме того, добавлены 2 метода AddAttributesToRender, OnPreRender. Первый метод добавляет атрибут ( Java script ) к HTML тэгу “input”, как показано на рисунке.

CheckBox.jpg

Данный атрибут содержит вызов функции onClick, когда мы устанавливаем или сбрасываем Auto Web контроллер. Реализация функции onClick дана в файле JavaScript.js который надо добавить к проекту ( смотри нижеследующий рисунок ).

JavaScript.jpg

Свойство Build Action устанавливаем к Embedded Recource ( обведено синим овалом ). Это позволяет поместить исходный текст функции onClick в ассамблею CheckBoxNik.dll, получающуся в результате построения решения( Build Solution ) данного проекта. Далее помещаем CheckBoxNik.dll в директорию Bin сайта.
Данный Java script будет кэширован браузером при первой загрузке aspx страницы, что сокращает время загрузки последующих вызовов страницы. Функция onClick имеет 2 параметра. В первом передается обьект Auto, в втором - идентификатор Web контроллера Price. Клиентский идентификатор Web контроллера, помещенного в DataGrid, формируется из 3 частей: идентификатора DataGrid Web контроллера, номера строки, в которой находится контроллер, идентификатора самого контроллера. Все три части разделены символом подчеркивания, как например, ItemsGrid_ctl02_AutoCheckBoxID. Зная это, функция onClick вычисляет клиентский идентификатор Web контроллера Price, после чего находим обьект Price. Устанавливаем свойство disabled этого обьекта в зависимости от значения обьекта Auto. Идентификатор Web контроллера Price передается в функцию onClick с помощью свойства TargetControlID, значение которого устанавливается в Auto CheckBox Web контроллере ( смотри нижеследующий код ).

Код:
<datasoft:CheckBox ID="AutoCheckBoxID" TargetControlID="PriceTextBoxID" runat="server" AutoPostBack=false Checked='<%# DataBinder.Eval(Container.DataItem,"auto")%>'/>

Данный контроллер необходимо зарегестрировать в начале aspx страницы ( смотри нижеследующий код ).

Код:
<%@ Register Assembly="CheckBoxNik" Namespace="CheckBoxNik" TagPrefix="datasoft" %>

В второй части статьи я покажу как осуществить доступ к ассамблее CheckBoxNik.dll посредством URL.
 
Как насчет генерации JScript кода из C# ? Позволяет практически избавиться от скриптов а программировать подобную логику на более-меннее нормальном языке :blink:
 
2 Dr.Gigabit: Автор, судя по всему, именно про это и пытается рассказать. Про то, как найти в DOM объекты, соответствующие серверным контролам. И про то, как привязать к ним скриптовые обработчики событий.

Немного непонятно, почему Control переведен как контроллер.
<!--QuoteBegin-NikSoft+31:12:2006, 07:45 -->
<span class="vbquote">(NikSoft @ 31:12:2006, 07:45 )</span><!--QuoteEBegin-->Клиентский идентификатор Web контроллера, помещенного в DataGrid, формируется из 3 частей: идентификатора DataGrid Web контроллера, номера строки, в которой находится контроллер, идентификатора самого контроллера. Все три части разделены символом подчеркивания, как например, ItemsGrid_ctl02_AutoCheckBoxID. Зная это, функция onClick вычисляет клиентский идентификатор Web контроллера Price, после чего находим обьект Price.
[snapback]52144" rel="nofollow" target="_blank[/snapback]​
[/quote]
По теме: а если грид, будет лежать не просто на странице, а внутри, скажем, PlaceHolder-а (или любого другого контрола с интерфейсом INamingContainer)? ClientID у чекбокса не поменяется? И не слишком ли это смело - считать, что механизим генерации "клиентского идентификатора Web контроллера Price" останется тем же в, скажем, .net 4.0.
 
Я несколько не то имел ввиду под генерацией...В примере получается что-то вроде обратного, т.е. привазяка jscript-C# обработчики.

Что касается ClientID -- внутри AjaxControl Toolkint такой же финт ушами делается, причем от хардкондых айдишников в глазах рябит. По опыту -- при переходе от 1.1 к 2.0 были проблемы с подобным трюком :)
 
Для: NikSoft
Код:
Page.ClientScript.RegisterClientScriptInclude("CheckBoxNik", Page.ClientScript.GetWebResourceUrl(this.GetType(), "CheckBoxNik.JavaScript.js"));
Не слишком ли тяжело таскть с собой скриптовый файл?

ItemsGrid_ctl02_AutoCheckBoxID
Мы бУдИм вас убИвать... ХА-ха-ХА...

Для: Dr.Gigabit
касается ClientID -- внутри AjaxControl Toolkint такой же финт ушами делается, причем от хардкондых айдишников в глазах рябит.
убИвать... ХА-ха-ХА...

Я несколько не то имел ввиду под генерацией...В примере получается что-то вроде обратного, т.е. привазяка jscript-C# обработчики.
Ничо не понял. обработчики всё равно остаются клиентские, JavaStript-овые.

Для: Pasha
Немного непонятно, почему Control переведен как контроллер.
Поддерживаю.

Как только страница переходит на поддержку ATLAS Beta 1. Пиши-пропало. Не продашь ты NikSoft свой контрол - работать не будет. Надеюсь, до релиза atlas починят. А пока перуэты в воздухе с тройным подвыпедвертом - на GET запросе регистрируешь скрипт с помощью стандартного ClientScriptManager-а, а на POST регистрируешь этот скрипт с помощь атласовского ClientScriptManager-а. Жаль примера под рукой нет.
 
<!--QuoteBegin-karlito+10:01:2007, 00:41 -->
<span class="vbquote">(karlito @ 10:01:2007, 00:41 )</span><!--QuoteEBegin-->Не слишком ли тяжело таскть с собой скриптовый файл?
[snapback]52658" rel="nofollow" target="_blank[/snapback]​
[/quote]
Нет, вполне обычная практика. Очень хорошо решает проблемы с подключением кучи скриптов. Тот же atlas так делает....

<!--QuoteBegin-karlito+10:01:2007, 00:41 -->
<span class="vbquote">(karlito @ 10:01:2007, 00:41 )</span><!--QuoteEBegin-->Как только страница переходит на поддержку ATLAS Beta 1. Пиши-пропало. Не продашь ты NikSoft свой контрол - работать не будет. Надеюсь, до релиза atlas починят. А пока перуэты в воздухе с тройным подвыпедвертом - на GET запросе регистрируешь скрипт с помощью стандартного ClientScriptManager-а, а на POST регистрируешь этот скрипт с помощь атласовского ClientScriptManager-а. Жаль примера под рукой нет.
[snapback]52658" rel="nofollow" target="_blank[/snapback]​
[/quote]
Это они вроде почти вылечили в atlas beta2/rc1. По крайней мере, теперь глюки не так часто проявляются.

IMHO, статью следовало бы переименовать в что-то вроде "как связать код JavaScript и серверные элементы управления ASP.NET".
 
Для: NikSoft
пример (не очень аккуратный, нарисован только для демонстрации)
Код:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Sample Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataGrid ID="TestGrid" runat="server" AutoGenerateColumns="false" ShowHeader="false" OnItemDataBound="TestGrid_ItemDataBound">
<Columns>
<asp:TemplateColumn>
<ItemTemplate>
<asp:TextBox ID="textBoxToTarget" runat="server" />
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn>
<ItemTemplate>
<asp:Button ID="buttonToAttachScript" runat="server" Text="Click Me" />
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
</div>
</form>
</body>
</html>
Код:
// usings here
public partial class _Default : System.Web.UI.Page 
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
TestGrid.DataSource = new int[5];
TestGrid.DataBind();
}
}

private string scriptTemplate = "document.getElementById('{0}').value = 'blah-blah-blah'; return false;";
protected void TestGrid_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Control textBoxControl = e.Item.Cells[0].FindControl("textBoxToTarget");
Button button = (Button)e.Item.Cells[1].FindControl("buttonToAttachScript");
button.OnClientClick = String.Format(scriptTemplate, textBoxControl.ClientID);
}
}
}