Пользовательские элементы управления
Управление динамической загрузкой многих пользовательских элементов управления (портальные каркасы)
Итак, мы разработали несколько пользовательских элементов управления:
- Header.ascx - верхний колонтитул
- TimeDisplay.ascx - кнопка-ссылка, возвращающая неформатированную и форматированную дату-время
- LinkTable.ascx - список гиперссылок
- LinkTableExt.ascx - список гиперссылок с предварительным анализом выбора пользователя (для этого создали свое событие)
- MyControl.ascx - с применением промежуточных средств доступа к настройкам внутренних компонентов
- MyControlExt.ascx - с созданием общего свойства-ссылки на встроенный компонент для доступа к любым его членам
- MyControlDynamic.ascx - то же самое, что и MyControlExt.ascx, только применяли способ динамической загрузки пользовательского элемента на тестовую страницу
Для каждого из этих элементов управления разрабатывалась своя тестовая страница, на которую элемент управления помещался декларативно. Только в последнем случае мы сам пользовательский элемент не изменяли, но на тестовой странице создавали его динамически (программно). Теперь давайте на примере рассмотрим вопрос, когда на страницу нужно поместить программно сразу несколько разных пользовательских элементов управления.
-
Создайте новую страницу DynamicUserControls.aspx с раздельным кодом и сделайте ее стартовой
-
В раскрывающемся списке панели Properties выберите элемент DOCUMENT и задайте его свойству Title значение Портальные каркасы
-
В режиме Design поместите с вкладки Standard элемент Panel и присвойте ему имя Panel1 (оно генерируется автоматически). Этот элемент будет служить контейнером для других элементов управления, в том числе и для пользовательского
-
Поместите внутрь контейнера Panel1 друг за другом по вертикали стандартные элементы
- DropDownList с автоматически сгенерированным именем DropDownList1 - для выбора загружаемого в контейнер пользовательского элемента управления
- PlaceHolder с автоматически сгенерированным именем PlaceHolder1 - для позиционирования в контейнере пользовательского элемента управления
- Label с автоматически сгенерированным именем Label1 - для отображения имени файла, из которого в контейнер загружен пользовательский элемент управления
По большому счету нам в данном примере безразлично, какие имена будут иметь экземпляры дочерних элементов. Однозначно различать их в коде мы можем по найденным ссылкам. Ссылки, в свою очередь, будем искать в именованном контейнере по типу дочернего элемента, поскольку помещенные нами элементы имеют разный тип. Но сама оболочка Visual Studio 2005 требует, чтобы на странице не было декларативно объявляемых дескрипторов с одинаковыми значениями ID.
-
Проследите в дескрипторном представлении страницы, чтобы ни один из элементов, включая контейнер, не имел атрибутов Width и Height, ограничивающих свободную расширяемость дочерних элементов
Заметим, что контейнер Panel1, как родитель, по умолчанию будет определять основные свойства своих дочерних элементов, такие как цвет фона, цвет переднего плана, шрифт и т.д., если только мы не определим их явным образом.
Ранее мы перечислили пользовательские элементы управления, которые создали в данной работе. Выберем несколько из них для динамического размещения. Имена этих элементов нужно занести в список DropDownList1. Пусть это будут элементы:
- Header.ascx - Верхний колонтитул
- TimeDisplay.ascx - Кнопка 'время-дата'
- LinkTable.ascx - Список гиперссылок
- LinkTableExt.ascx - Контролируемый список гиперссылок
- MyControl.ascx - С настройкой внутренних компонентов
-
Выделите объект DropDownList1 в контейнере Place1 и заполните декларативно его свойство Items так
-
В скрытом поле Value мы указали имена файлов, которые в будущем должны будем создать
-
Установите для элемента DropDownList1 его свойство AutoPostBack в значение True, чтобы обеспечить постинг страницы при выборе пользователем нужной опции
Будем считать, что один из трех стандартных элементов управления контейнера, а именно список, мы заполнили декларативно. Два других элемента контейнера будем заполнять программно.
Таким образом, один контейнер мы почти настроили. Но мы хотим разместить на странице три таких контейнера, чтобы иметь возможность в каждый из них программно загружать выбранные через список DropDownList1 пользовательские элементы управления. При этом мы хотим убедиться, что они работают независимо друг от друга и каждый делает то, что должен путем реагирования на свои собственные события.
-
В режиме Design тестовой страницы разместите по вертикали друг за другом три копии контейнера Place1 вместе с его дочерними элементами. Проследите, чтобы контейнеры имели имена Panel1, Panel2, Panel3
-
Через раскрывающийся список панели Properties выделяйте последовательно каждый контейнер Panel1, Panel2, Panel3 и настройте их свойства так
Мы закончили этап проектирования страницы для данного примера. Дескрипторное представление страницы должно быть таким
<%@ Page AutoEventWireup="true" CodeFile="DynamicUserControls.aspx.cs" Inherits="DynamicUserControls" Language="C#" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Портальные каркасы</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Panel ID="Panel1" runat="server" BackColor="Gainsboro" BorderStyle="Ridge" BorderWidth="1px" HorizontalAlign="Center" EnableViewState="False"> <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="LinkTableExtTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder> <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="LinkTableExtTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder2" runat="server"></asp:PlaceHolder> <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="LinkTableExtTest.ascx">Контролируемый список гиперссылок</asp:ListItem> <asp:ListItem Value="MyControlTest.ascx">С настройкой внутренних компонентов</asp:ListItem> </asp:DropDownList><br /> <br /> <asp:PlaceHolder ID="PlaceHolder3" runat="server"></asp:PlaceHolder> <br /> <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label></asp:Panel> <br /> </div> </form> </body> </html>
-
Выполните сформированную страницу DynamicUserControls.aspx, чтобы убедиться в работоспособности настроек режима проектирования. Результат на данном этапе должен быть таким
Таким образом, мы выполнили интерфейсную заготовку портальных каркасов на тестовой странице. Теперь пришла пора наполнить их функциональностью, которая бы обеспечила контейнерную загрузку выбранных пользовательских элементов управления. Эту функциональность мы обеспечим, кодируя файл DynamicUserControls.aspx.cs скрытого кода страницы. Ниже мы убедимся, что программно загруженные в портальные каркасы пользовательские элементы управления обрабатывают свои события точно также, как будто бы каждый из них находится на отдельной тестовой странице.
-
Откройте на редактирование файл DynamicUserControls.aspx.cs
-
Добавьте в класс DynamicUserControls следующий код
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 DynamicUserControls : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Загружаем портальные каркасы MyLoadControls(Panel1); MyLoadControls(Panel2); MyLoadControls(Panel3); } // Вспомогательная функция private void MyLoadControls(Control containerPanel) { // Объявляем вспомогательные ссылки на интерфейсные // элементы текущего контейнера DropDownList list = null; PlaceHolder ph = null; Label lbl = null; // Перебираем все дочерние элементы текущего // контейнера и ищем ссылки на объекты дочерних // интерфейсных элементов по их типам foreach (Control ctrl in containerPanel.Controls) { if (ctrl is DropDownList) list = (DropDownList)ctrl; else if (ctrl is PlaceHolder) ph = (PlaceHolder)ctrl; else if (ctrl is Label) lbl = (Label)ctrl; } // Берем информацию из выделенного // элемента списка текущего контейнера string lstValue = list.SelectedItem.Value; string lstText = list.SelectedItem.Text; lbl.Text = ""; // Заполняем текущий контейнер. // Проверяем наличие нужного расширения if (lstValue.EndsWith(".ascx")) { ph.Controls.Add(Page.LoadControl(lstValue)); lbl.Text = "Загрузили: " + lstValue + " - " + lstText; } } }
Создадим пользовательские элементы управления, которые реализуют пользовательский интерфейс и функциональность, использованную нами при выполнении примеров в тестовых страницах. Из этих тестовых страниц мы их и переделаем.
-
В панели Solution Explore выделите при нажатой клавише Ctrl следующие файлы
- HeaderTest.aspx
- TimeDisplayTest.aspx
- LinkTableTest.aspx
- LinkTableExtTest.aspx
- MyControlTest.aspx
-
Выполните команду Copy затем Paste (на верхнем узле проекта) контекстного меню
-
Присвойте копиям те же самые имена, только с расширением .ascx
- HeaderTest.ascx
- TimeDisplayTest.ascx
- LinkTableTest.ascx
- LinkTableExtTest.ascx
- MyControlTest.ascx
-
Откройте новые файлы в дескрипторном режиме и замените директиву @Page на @Control, все остальные атрибуты директивы @Page оставьте прежними. Теперь это будут не страницы, а пользовательские элементы управления. Директиву @Register с ее атрибутами оставьте в покое
-
Удалить из кода все дескрипторы <html>, <body>, <form>, <head> и закрывающие их дескрипторы.
-
Для новых файлов *.ascx откройте файлы скрытого кода *.ascx.cs и замените в объявлении класса имя наследующего класса с " : System.Web.UI.Page " на " : System.Web.UI.UserControl ". Остальной код классов останется прежним.
Мы закончили выполнение примера. Код тестовой страницы при очередной загрузке будет программно создавать свои пользовательские элементы управления для каждого контейнера Panel и размещать их в элементах PlaceHolder.
-
Выполните страницу DynamicUserControls.aspx и испытайте ее. Результат будет примерно таким