Разработка на ASP.NET. MasterPage, динамические компоненты, AJAX, ASP.NET MVC
7.2. Динамическое создание элементов управления
Иногда в процессе работы над приложением возникает необходимость создания динамического интерфейса [4]. Это становится особенно актуально в случае, когда интерфейс приложения должен меняться в зависимости от действий пользователя. В этом случае удобным инструментом реализации динамического интерфейса становится возможность динамического создания и отображения на странице элементов управления. Для создания элемента управления необходимо создать объект соответствующего класса, присвоить его атрибутам необходимые значения и добавить его к коллекции элементов управления страницы. При этом следует помнить о том, что, так как в момент исполнения программного кода, создающего объект и добавляющего его к коллекции элементов управления, страница уже создана, то добавление будет всегда происходить в конец коллекции. Это означает, что элемент будет добавляться после последнего элемента управления страницы.
Для получения большего контроля над расположением динамически создаваемого элемента управления, можно воспользоваться специально предназначенным для этого элементом PlaceHolder [1]. Данный элемент управления предназначен для размещения других элементов внутри себя и может быть размещен в любом месте страницы. При этом если внутри PlaceHolder отсутствует содержимое, это никак не повлияет на содержимое страницы.
При использовании динамически создаваемых элементов управления необходимо помнить, что они существуют только до очередной обратной отсылки. Если после обратной отсылки динамически созданный элемент управления необходимо по-прежнему отображать на странице, необходимо создавать элемент управления в обработчике события Page.Load. При создании элемента управления в обработчике события Page.Load ASP.NET использует любую информацию о состоянии вида по завершении этого обработчика события.
Для возможности программного взаимодействия с динамически созданными элементом управления, последнему необходимо присвоить уникальный идентификатор ( ID ), являющийся аналогом ID любого другого элемента управления. В дальнейшем этот идентификатор можно использовать для того, чтобы найти данный элемент управления в коллекции элементов управления страницы с помощью метода Page.FindControl() и использовать в программном коде.
Расширим предыдущее приложение, переписав страницу следующим образом:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" MasterPageFile="~/Site.Master" %> <asp:Content ContentPlaceHolderID="MainPlaceHolder" ID="Content1" runat="server"> <asp:Button ID="ButtonAdd" runat="server" Text="Отобразить изображение" OnClick="OnClick" /> <asp:Literal ID="Literal1" runat="server"></asp:Literal> <asp:Panel runat="server" ID="ImagePanel"></asp:Panel> </asp:Content>
Теперь появилась кнопка, к которой можно привязать обработчик события нажатия кнопки мышкой OnClick. Когда пользователь нажмет кнопку, произойдет postback страницы. Как уже отмечалось раньше, страница на сервере восстановится, после чего будут вызваны все обработчики событий, в данном случае OnClick:
protected void OnClick(object sender, EventArgs e) { Image img = new Image(); img.ImageUrl = "~/Black_racer02_F_large.gif"; ImagePanel.Controls.Add(img); }
Этот код создает новый объект Image (в разметке это <asp:Image> ), у которого будет задано лишь одно свойство – ImageUrl. После этого этот компонент будет добавлен с коллекцию Controls панели ImagePanel. Теперь, на этапе Render страницы, в разметку будет добавлен новый элемент img с указанным изображением:
Результат работы программы изображен на рис. 7.5.
Такой динамически созданный компонент не будет обрабатывать события, так как при следующем postback' е он не будет восстановлен. Более того, если бы на странице был еще какой-либо объект, вызывающий postback, то после отправки запроса на сервер изображение пропало бы. Для того чтобы динамически созданные элементы управления могли обрабатывать события, необходимо создавать эти компоненты во время Page.Load или переопределив метод CreateChildControls. При этом чтобы предыдущий пример работал, необходимо во ViewState или в состоянии сеанса сохранить информацию о том, что изображение необходимо добавлять на страницу.
Иногда возникает необходимость изменять содержимое таблицы на основе производимых пользователем действий. В этом случае обойтись статическими элементами, вводимыми в таблицу на этапе разработки приложения невозможно. Решить данную задачу можно воспользовавшись механизмом добавления элементов в таблицу во время выполнения. Здесь следует отметить, что элемент управления Table не сохраняет те элементы, которые добавляются в него во время выполнения приложения. Это приводит к тому, что таблицу необходимо заново создавать на основе данных, сохраненных в переменной состояния.
Еще раз доработаем пример. Пусть теперь на странице расположена таблица, у которой установлена строка заголовка и первая строка содержимого. Пользователь имеет возможность добавлять новые строки. Для этого он должен внести содержимое новой строки в поле ввода, разделяя значения ячеек запятыми. После нажатия на кнопку "Добавить" новая строка добавляется к уже существующим строкам таблицы.
Перепишем разметку страницы:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" MasterPageFile="~/Site.Master" %> <asp:Content ContentPlaceHolderID="MainPlaceHolder" ID="Content1" runat="server"> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button ID="ButtonAdd" runat="server" Text="Отобразить изображение" OnClick="OnClick" /> <asp:Table runat="server" ID="Table1"> <asp:TableHeaderRow> <asp:TableHeaderCell> Идентификатор </asp:TableHeaderCell> <asp:TableHeaderCell> Наименование </asp:TableHeaderCell> <asp:TableHeaderCell> Количество </asp:TableHeaderCell> </asp:TableHeaderRow> <asp:TableRow> <asp:TableCell>1</asp:TableCell> <asp:TableCell>Велосипед</asp:TableCell> <asp:TableCell>10</asp:TableCell> </asp:TableRow> </asp:Table> </asp:Content>
Теперь таблица представлена компонентом <asp:Table>, который при отрисовке страницы будет заменен элементом table. Также добавлено текстовое поле TextBox1, в которое пользователь будет вводить данные.
Исходный код серверных обработчиков страницы с комментариями представлен ниже:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebApplication1 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void OnClick(object sender, EventArgs e) { //Записать текст в состояние отображения страницы, //причем новые данные будут сохранены под индексом ViewState. //Count, чтобы не потерять предыдущее данные ViewState.Add(ViewState.Count.ToString(), TextBox1.Text); CreateTable(); } private void CreateTable() { //массив слов string[] arrWords; //строка, состоящая из значений ячеек строки таблицы string strWords; TableRow newRow; TableCell newCell; //цикл для каждого элемента ViewState for (int i = 0; i < ViewState.Count; i++) { //символ-разделитель значений полей таблицы char[] c = { ',' }; //создать новую строку newRow = new TableRow(); //извлечь из ViewState очередной элемент strWords = ViewState[i.ToString()].ToString(); //преобразовать строку в массив используя разделитель arrWords = strWords.Split(c); //цикл для каждого элемента массива слов for (int j = 0; j <= arrWords.GetUpperBound(0); j++) { //создать новую ячейку таблицы newCell = new TableCell(); //установить текст в созданную ячейку newCell.Text = arrWords[j]; //добавить ячейку к набору ячеек строки таблицы newRow.Cells.Add(newCell); } //добавить строку к таблице Table1.Rows.Add(newRow); } } } }
Результат работы кода страницы представлен на рис. 7.6.