Украина, Киев |
Пользовательские элементы управления
Упражнение 5. Динамическое создание пользовательского элемента управления
На протяжении этой темы мы рассматривали вопрос создания пользовательского элемента управления в режиме проектирования и последующее управление им в программном режиме. Но может потребоваться на странице подгружать и настраивать пользовательский элемент управления динамически ("на лету").
Для динамического создания пользовательского элемента управления ребуется выполнить следующие шаги:
- Создавать при каждой загрузке страницы в обработчике ее события Page.Load экземпляр класса пользовательского элемента управления, заранее подготовленного в файлах *.ascx и *.ascx.cs. Создание и регистрация экземпляра пользовательского элемента в коде исполнимой страницы осуществляется статическим методом Page.LoadControl().
- Резервировать с помощью элемента PlaceHolder место размещения для точного позиционирования на странице пользовательского элемента управления.
- Обязательно присваивать после динамического создания значение свойства ID пользовательского элемента управления, особенно, когда создается несколько экземпляров одного и того же элемента. Это может понадобиться для однозначной идентификации элемента в коде страницы при поиске ссылки на экземпляр, выполняемом с помощью статического метода Page.FindControl(), который как раз использует значение идентификатора ID.
Есть некоторая специфика динамического создания пользовательского элемента управления по сравнению с библиотечным компонентом. Нельзя создать пользовательский элемент напрямую, как это делается с библиотечным элементом управления через конструктор класса. Пользовательский элемент управления состоит не только из поддерживающего класса в файле *.ascx.cs, но и дескрипторной части файла *.ascx. Поэтому на этапе проектирования он не представлен общим классом, экземпляр которого можно было-бы создать с помощью конструктора. Экземпляр пользовательского элемента управления создается самой ASP.NET объединением этих двух частей на этапе выполнения.
- Скопируйте через панель Solution Explorer, как это мы делали ранее, файл тестовой страницы MyControlTestExt.aspx и файл пользовательского элемента управления MyControlExt.ascx
- Присвойте копиям новые имена MyControlDynamicTest.aspx и MyControlDynamic.ascx соответственно (застраничные файлы оболочка переименует автоматически)
- Сделайте страницу MyControlDynamicTest.aspx стартовой, выполнив на ее узле в панели Solution Explorer команду Set As Start Page
- Откройте файл MyControlDynamic.ascx в режиме Source и исправьте в директиве @Control атрибут подключаемого класса так Inherits =" MyControlDynamic "
- Откройте застраничный файл MyControlDynamic.ascx.cs и измените имя класса на MyControlDynamic
- Закройте все предыдущие редактируемые документы командой меню Window/Close All Documents
- Откройте на редактирование страницу MyControlDynamicTest.aspx в режиме Design, удалите наш пользовательский элемент управления Label1 и поместите на его место из вкладки Standard стандартный элемент управления PlaceHolder
- Перейдите в файле MyControlDynamicTest.aspx в режим Source редактора страницы, откорректируйте атрибут Inherits =" MyControlDynamicTest " и удалите полностью старый код директивы регистрации
<%@ Register Src="MyControlExt.ascx" TagName="MyControl" TagPrefix="uc1" %>
- В этом же файле MyControlDynamicTest.aspx в режиме Source удалите из дескриптора <asp:RadioButtonList> атрибут регистрации обработчика
OnSelectedIndexChanged="RadioButtonList1_SelectedIndexChanged"
На данном этапе страница в дескрипторном представлении будет такой
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MyControlDynamicTest.aspx.cs" Inherits="MyControlDynamicTest" %> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:PlaceHolder ID="PlaceHolder1" runat="server" /> <br /> <br /> <asp:RadioButtonList ID="RadioButtonList1" runat="server" RepeatDirection="Horizontal"> <asp:ListItem Value="Red">Красный</asp:ListItem> <asp:ListItem Value="Green">Зеленый</asp:ListItem> <asp:ListItem Value="Blue">Синий</asp:ListItem> </asp:RadioButtonList> <br /> <input id="Submit1" type="submit" value="submit" /> </div> </form> </body> </html>Листинг 32.25. Страница MyControlDynamicTest.aspx после удаления пользовательского элемента
Теперь пользовательский элемент управления на странице не существует, а вместо него вставлен заменитель пустого места PlaceHolder1. Далее мы программно должны создать пользовательский элемент, добавить ссылку на него к странице, а затем можно управлять свойствами его компонентов.
- Откройте на редактирование застраничный MyControlDynamicTest.aspx.cs, выполнив команду контекстного меню View Code редактора
- Измените код обработчика 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 MyControlDynamicTest : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Создаем экземпляр класса поддержки // пользовательского элемента управления MyControlDynamic MyControl1 = (MyControlDynamic) Page.LoadControl("MyControlDynamic.ascx"); // Добавляем объект в зарезервированное место страницы PlaceHolder1.Controls.Add(MyControl1); 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.26. Код создания пользовательскогоэлемента в файле MyControlDynamicTest.aspx.cs
Обратите внимание, что при наборе кода создания пользовательского элемента в редакторе кода механизм IntelliSense нам не помогает, потому что страница не знает, что где-то в файле MyControlDynamic.ascx существует класс MyControl.
- Запустите страницу MyControlDynamicTest.aspx на выполнение и убедитесь в работоспособности результата, как будто-бы пользовательский элемент был помещен на исполнимую страницу в декларативном режиме
Упражнение 6. Управление динамической загрузкой многих пользовательских элементов управления (портальные каркасы)
Итак, мы разработали несколько пользовательских элементов управления:
- Header.ascx - верхний колонтитул
- TimeDisplay.ascx - кнопка-ссылка, возвращающая неформатированную и форматированную дату-время
- LinkTable.ascx - список гиперссылок с предварительным анализом выбора пользователя (для этого создали свое событие)
- MyControl.ascx - с применением промежуточных средств доступа к настройкам внутренних компонентов
- MyControlExt.ascx - с созданием общего свойства-ссылки на встроенный компонент для доступа к любым его членам
- MyControlDynamic.ascx - то же самое, что и MyControlExt.ascx, только применяли способ динамической загрузки пользовательского элемента на тестовую страницу
Для каждого из этих элементов управления разрабатывалась своя тестовая страница, на которую элемент управления помещался декларативно. Только в последнем случае мы сам пользовательский элемент не изменяли, но на тестовой странице создавали его динамически (программно).
В этом упражнении рассмотрим вопрос, когда на одну страницу нужно поместить программно сразу несколько разных пользовательских элементов управления.
- Создайте новую страницу DynamicUserControls.aspx с раздельным кодом и сделайте ее стартовой
- В раскрывающемся списке панели Properties выберите элемент DOCUMENT и задайте его свойству Title значение Портальные каркасы
- В режиме Design поместите с вкладки Standard элемент Panel и присвойте ему имя Panel1 (оно генерируется автоматически). Этот элемент будет служить контейнером для других элементов управления, в том числе и для пользовательского
-
Поместите
внутрь контейнера Panel1 друг за другом по вертикали стандартные
элементы
- DropDownList с автоматически сгенерированным именем DropDownList1 - для выбора загружаемого в контейнер пользовательского элемента управления
- PlaceHolder с автоматически сгенерированным именем PlaceHolder1 - для позиционирования в контейнере пользовательского элемента управления
Интерфейс страницы на этапе проектирования должен быть примерно таким
По большому счету нам в данном примере безразлично, какие имена будут иметь экземпляры дочерних элементов. Однозначно различать их в коде мы можем по найденным ссылкам. Ссылки, в свою очередь, будем искать в именованном контейнере-панели по типу дочернего элемента, поскольку помещенные нами элементы имеют разный тип. Но сама оболочка Visual Studio 2005 требует, чтобы на странице не было декларативно объявляемых дескрипторов с одинаковыми значениями ID.
- Проследите в дескрипторном представлении страницы, чтобы ни один из элементов, включая контейнер, не имел атрибутов Width и Height, ограничивающих свободную расширяемость дочерних элементов
Заметим, что контейнер Panel1, как родитель, по умолчанию будет определять основные свойства своих дочерних элементов, такие как цвет фона, цвет переднего плана, шрифт и т.д., если только мы не определим их в элементах явным образом индивидуально.
Ранее мы перечислили пользовательские элементы управления, которые создали в данной работе. Выберем несколько их них для динамического размещения. Имена этих элементов нужно занести в список DropDownList1. Пусть это будут элементы:
- Header.ascx - Верхний колонтитул
- TimeDisplay.ascx - Кнопка 'время-дата'
- LinkTable.ascx - Контролируемый список гиперссылок
- MyControl.ascx - С настройкой внутренних компонентов
- Выделите объект DropDownList1 в контейнере Place1 и заполните декларативно его свойство Items так
В скрытом поле Value мы указали имена файлов, которые в будущем должны будем создать.
- Установите для элемента DropDownList1 его свойство AutoPostBack в значение True, чтобы обеспечить постинг страницы при выборе пользователем нужной опции
Будем считать, что один из трех стандартных элементов управления контейнера, а именно список, мы заполнили декларативно. Два других элемента контейнера будем заполнять программно.
Таким образом, один контейнер мы почти настроили. Но мы хотим разместить на странице дополнительно еще два таких контейнера, чтобы иметь возможность в каждый из них программно загружать выбранные через список DropDownList1 пользовательские элементы управления. При этом мы хотим убедиться, что они работают независимо друг от друга и каждый делает то, что должен путем реагирования на свои собственные события.
- Через раскрывающийся список панели Properties выделите контейнер Panel1 и настройте его свойства так
- В режиме Design разрабатываемой страницы DynamicUserControls.aspx разместите на ней по вертикали друг за другом еще две копии контейнера Place1 вместе с его дочерними элементами и присвойте им имена Panel2, Panel3
Мы закончили этап проектирования страницы для данного упражнения. Интерфейсная часть на этапе проектирования будет выглядеть так
Дескрипторное представление страницы должно быть таким
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DynamicUserControls.aspx.cs" Inherits="DynamicUserControls" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Портальные каркасы</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Panel ID="Panel1" runat="server" BackColor="Gainsboro" BorderStyle="Ridge" BorderWidth="1px" EnableViewState="False" HorizontalAlign="Center"> <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True"> <asp:ListItem Value="None">(None)</asp:ListItem> <asp:ListItem Value="HeaderTest.ascx">Верхний колонтитул</asp:ListItem> <asp:ListItem Value="TimeDisplayTest.ascx">Кнопка 'время-дата'</asp:ListItem> <asp:ListItem Value="LinkTableTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder> <br /> <br /> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></asp:Panel> <br /> <asp:Panel ID="Panel2" runat="server" BackColor="Gainsboro" BorderStyle="Ridge" BorderWidth="1px" EnableViewState="False" HorizontalAlign="Center"> <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True"> <asp:ListItem Value="None">(None)</asp:ListItem> <asp:ListItem Value="HeaderTest.ascx">Верхний колонтитул</asp:ListItem> <asp:ListItem Value="TimeDisplayTest.ascx">Кнопка 'время-дата'</asp:ListItem> <asp:ListItem Value="LinkTableTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder2" runat="server"></asp:PlaceHolder> <br /> <br /> <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label></asp:Panel> <br /> <asp:Panel ID="Panel3" runat="server" BackColor="Gainsboro" BorderStyle="Ridge" BorderWidth="1px" EnableViewState="False" HorizontalAlign="Center"> <asp:DropDownList ID="DropDownList3" runat="server" AutoPostBack="True"> <asp:ListItem Value="None">(None)</asp:ListItem> <asp:ListItem Value="HeaderTest.ascx">Верхний колонтитул</asp:ListItem> <asp:ListItem Value="TimeDisplayTest.ascx">Кнопка 'время-дата'</asp:ListItem> <asp:ListItem Value="LinkTableTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder3" runat="server"></asp:PlaceHolder> <br /> <br /> <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label></asp:Panel> </div> </form> </body> </html>Листинг 32.27. Дескрипторное представление страницы DynamicUserControls.aspx
- Выполните сформированную страницу DynamicUserControls.aspx, чтобы убедиться в работоспособности настроек режима проектирования
Мы выполнили интерфейсную заготовку портальных каркасов на исполнимой странице. Теперь пришла пора наполнить их функциональностью, которая бы обеспечила контейнерную загрузку выбранных пользовательских элементов управления. Эту функциональность мы обеспечим, кодируя застраничный файл DynamicUserControls.aspx.cs.