Пользовательские элементы управления
Упражнение 4. Доступ из кода Web-страницы к составным компонентам пользовательского элемента управления
Web-страница, которая содержит интегрированный в нее пользовательский элемент управления, не может без дополнительных приемов получать доступ к компонентам этого элемента: к его методам, событиям, свойствам. Такое положение является обоснованным, чтобы случайно в Web-странице не внести нежелательные изменения в ее дочерний элемент.
Но учитывая, что сам пользовательский элемент имеет доступ к своим компонентам, а страница имеет доступ к пользовательскому элементу, это правило можно обойти. Для этого существует два способа:
- Ввести в класс пользовательского элемента дополнительные свойства и функции доступа к нужным членам индивидуально для каждого компонента
- Ввести в класс пользовательского элемента управления для каждого компонента по одному дополнительному свойству, возвращающему ссылку на соответствующий компонент
Способ 1
В пользовательский элемент вводят промежуточные свойства или методы доступа, через которые из Web-страницы обращаются к его компонентам. Это напоминает прием, который применяется в C++ для поддержания инкапсуляции (закрытости) данных: переменные-члены класса с модификатором доступа private упаковывают в функции доступа public, которые сами видят эти переменные, а их, в свою очередь, видят клиенты этого класса.
Пусть, например, пользовательский элемент управления MyControl1 как объект порождается классом MyControl и в качестве одного из компонентов содержит в себе объект Label1 библиотечного класса System.Web.UI.WebControls.Label. Класс Label наследует от базового класса System.Web.UI.WebControls.WebControl свойство BackColor, которое, в свою очередь, имеет тип System.Drawing.Color. Напрямую из Web-страницы нельзя установить новое значение свойства BackColor компонента Label1 пользовательского элемента. Но можно открыть доступ к этому свойству для Web-страницы, введя промежуточное свойство BackColor (или с другим именем) в классе самого пользовательского элемента. Сказанное схематично изображено на рисунке
Выполним это на практике.
- Создайте заготовку пользовательского элемента Web User Control с именем MyControl.ascx с раздельным кодом (флажок Place code in separate file мастера включен)
- Перейдите в режим Design редактора заготовки и поместите на нее из вкладки Standard элемент Label с именем Label1
- В панели Solution Explorer откройте файл отделенного кода MyControl.ascx.cs, удалите из класса MyControl заготовку обработчика Page_Load() за ненадобностью и дополните класс свойствами доступа, чтобы он стал таким
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class MyControl : System.Web.UI.UserControl { public string Text { get { return Label1.Text; } set { Label1.Text = value; } } public System.Drawing.Color BackColor { get { return Label1.BackColor; } set { Label1.BackColor = value; } } public System.Drawing.Color ForeColor { get { return Label1.ForeColor; } set { Label1.ForeColor = value; } } }Листинг 32.20. Добавление свойств доступа к классу MyControl
Здесь для свойств доступа мы использовали имена точно такие, как у прямых свойств компонента Label1. Но это не обязательно, можно использовать любые имена.
- Создайте пользовательскую тестовую страницу с раздельным кодом под именем MyControlTest.aspx и переведите ее в режим редактирования Design
- Из панели Solution Explorer из дерева файлов приложения перенесите на тестовую страницу узел MyControl.ascx. Убедитесь, что экземпляр пользовательского элемента управления зарегистрировался на тестовой странице под именем MyControl1
- Попробуйте выделить добавленный пользовательский элемент управления и убедитесь, что он не выделяется и добавленные нами свойства не видны в панели Properties
Если пытаться устанавливать начальные настройки пользовательского элемента через атрибуты его дескриптора, то подсказчик кода сообщит, что это возможно, поскольку введенные нами вспомогательные свойства доступны из клиентской страницы
Аналогичным образом, введенные свойства доступны и из программного кода клиентской страницы через имя экземпляра пользовательского элемента, в чем мы сейчас и убедимся на практике.
- Поместите на страницу под пользовательским элементом управления компонент RadioButtonList из вкладки Standard и убедитесь, что он имеет ID="RadioButtonList1"
- Через коллекцию Items элемента RadioButtonList1 задайте три радиокнопки с такими свойствами
- Установите свойство RepeatDirection элемента RadioButtonList1 в значение Horozontal
- Выделите компонент RadioButtonList1 и через панель Properties создайте обработчик для его события SelectedIndexChanged
- Под списком радиокнопок поместите компонент Input (Submit) из вкладки HTML
Дескрипторное представление страницы будет иметь вид
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MyControlTest.aspx.cs" Inherits="MyControlTest" %> <%@ Register Src="MyControl.ascx" TagName="MyControl" TagPrefix="uc1" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <uc1:MyControl ID="MyControl1" runat="server" /> <br /> <br /> <asp:RadioButtonList ID="RadioButtonList1" runat="server" RepeatDirection="Horizontal" OnSelectedIndexChanged="RadioButtonList1_SelectedIndexChanged"> <asp:ListItem Value="Red">Красный</asp:ListItem> <asp:ListItem Value="Green">Зеленый</asp:ListItem> <asp:ListItem Value="Blue">Синий</asp:ListItem> </asp:RadioButtonList></div> <br /> <input id="Submit1" type="submit" value="submit" /> </form> </body> </html>Листинг 32.21. Дескрипторное представление страницы MyControlTest.aspx
Мы хотим, чтобы на клиенте при щелчке на кнопке Submit выполнялась обратная отсылка на сервер, где код страницы через введенные нами в класс пользовательского элемента управления дополнительные свойства менял бы настройку элемента Label1. Этот код смены настройки нужно поместить в созданный нами обработчик радиокнопок, чтобы он срабатывал каждый раз, когда получит обратную отсылку с требованием пользователя выполнить новые настройки. В результате элемент Label1 сгенерирует нужный HTML-код и отошлет его клиенту.
- Заполните обработчик RadioButtonList1_SelectedIndexChanged() страницы в файле MyControlTest.aspx.cs следующим кодом
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class MyControlTest : System.Web.UI.Page { protected void RadioButtonList1_SelectedIndexChanged(object sender, EventArgs e) { int index = RadioButtonList1.SelectedIndex; switch (index) { case 0: MyControl1.ForeColor = System.Drawing.Color.White; MyControl1.BackColor = System.Drawing.Color.Red; break; case 1: MyControl1.ForeColor = System.Drawing.Color.Aqua; MyControl1.BackColor = System.Drawing.Color.Green; break; case 2: MyControl1.ForeColor = System.Drawing.Color.Yellow; MyControl1.BackColor = System.Drawing.Color.Blue; break; } if(index != -1) MyControl1.Text = RadioButtonList1.Items[index].Value; } }Листинг 32.22. Код обработчика в файле MyControlTest.aspx.cs
- Выполните страницу и убедитесь, что при щелчке на кнопке код исполнимой страницы меняет свойства компонента Label1 через добавленные нами дополнительные глобальные свойства
Способ 2
Есть еще один способ обеспечить доступ к компонентам, интегрированным в пользовательский элемент управления, это добавить в пользовательский элемент по дополнительному свойству, возвращающее ссылку на каждый компонент. При этом нужно использовать только get -аксессоры, поскольку адреса компонентов менять нельзя. В дальнейшем через это свойство-ссылку, доступную из страницы, можно обратиться к любым членам раскрытого таким образом компонента, входящего в состав пользовательского элемента.
- Скопируйте через панель Solution Explorer, как это мы делали ранее, файл исполнимой страницы MyControlTest.aspx и файл пользовательского элемента управления MyControl.ascx
- Присвойте копиям новые имена MyControlTestExt.aspx и MyControlExt.ascx соответственно (сопровождающие застраничные кодовые файлы оболочка переименует автоматически)
- Сделайте страницу MyControlTestExt.aspx стартовой, выполнив на ее узле в панели Solution Explorer команду Set As Start Page
- Измените в файле MyControlTestExt.aspx значение параметра Inherits на Inherits="MyControlTestExt" директивы @Page
- Измените параметр Src директивы регистрации в файле MyControlTestExt.aspx
<%@ Register Src="MyControlExt.ascx" TagName="MyControl" TagPrefix="uc1" %>
- Откройте файл скрытого кода MyControlExt.ascx.cs и измените в нем класс MyControl, чтобы он выглядел так
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class MyControlExt : System.Web.UI.UserControl { // Раскрываем весь компонент Label1 // Имя свойства InnerLabel1 выбрали произвольно // Используется только get-аксессор public Label InnerLabel1 { get { return Label1; } } }Листинг 32.23. Добавление свойства-ссылки в файле MyControlExt.ascx.cs
- Скорректируйте в файле MyControlExt.ascx атрибут директивы Control так Inherits="MyControlExt"
- Откройте файл MyControlTestExt.aspx.cs скрытого кода тестовой страницы и отредактируйте его для доступа к прямым свойствам компонента Label1 с использованием созданного адреса InnerLabel1
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class MyControlTestExt : System.Web.UI.Page { protected void RadioButtonList1_SelectedIndexChanged(object sender, EventArgs e) { int index = RadioButtonList1.SelectedIndex; // Доступ к прямым свойствам компонента Label1 выполняем // через ссылку на этот компонент в родителе MyControl1 switch (index) { case 0: MyControl1.InnerLabel1.ForeColor = System.Drawing.Color.White; MyControl1.InnerLabel1.BackColor = System.Drawing.Color.Red; break; case 1: MyControl1.InnerLabel1.ForeColor = System.Drawing.Color.Aqua; MyControl1.InnerLabel1.BackColor = System.Drawing.Color.Green; break; case 2: MyControl1.InnerLabel1.ForeColor = System.Drawing.Color.Yellow; MyControl1.InnerLabel1.BackColor = System.Drawing.Color.Blue; break; } if(index != -1) MyControl1.InnerLabel1.Text = RadioButtonList1.Items[index].Value; } }Листинг 32.24. Доступ к Label1 через адрес InnerLabel1 на странице MyControlTestExt.aspx
- Выполните страницу и убедитесь, что получился тот же самый результат, что и в предыдущем примере при реализации первого способа
Какой способ выбрать - решает программист!