Недавно решил изучить ASP.NET и одновременно создать что-нибудь полезное, посложнее «Hello, world!». Первое знакомство с ASP.NET у меня началось с IBuySpy. Довольно быстро разобравшись с этим Shared Source проектом, я начал его перекраивать под свои нужды. В итоге осталось довольно мало оригинального кода, кроме части ядра (ибо, зачем изобретать велосипед? :)). Кроме всего прочего, хотелось сделать больше функциональности, например, всплывающее меню. В сети много примеров всяких всплывающих менюшек, в том числе и исходников Open Source. Но они, как правило, сложны и не очень подходили под мои нужды. Поэтому я решил написать простое меню в виде пользовательского элемента и предложить Вам результаты своего творчества. В результате должно получиться это:
1. Реализация базы данных
Начнем с написания базы данных. Для этой статьи я немного переделал таблицу Tabs из IBuySpy:
CREATE TABLE [Tabs] ( [TabID] [int] IDENTITY (1, 1) NOT NULL, [TabOrder] [int] NOT NULL, [TabName] [nvarchar] (50) NOT NULL, [ParentTab] [int] NOT NULL ) ON [PRIMARY] GO
TabID – идентификатор закладки,
TabOrder – порядковый номер закладки,
TabName – имя закладки,
ParentTab – указывает на идентификатор родительской закладки или имеет -1, если это верхний уровень меню.
Также необходимо написать пару процедур. Одну для составления списка меню из закладок (Tabs):
CREATE PROCEDURE GetMenuItems AS SELECT TabID, TabOrder, TabName FROM Tabs WHERE ParentTab = -1 ORDER BY TabOrder GO
И еще одну для получения подменю для текущей закладки:
CREATE PROCEDURE GetSubMenuItems ( @ParentTab int ) AS SELECT TabID, TabOrder, TabName FROM Tabs WHERE ParentTab = @ParentTab ORDER BY TabOrder GO
Надеюсь, тут все ясно. В принципе, можно обойтись только второй процедурой, передавая ей в качестве параметра -1 для заглавного меню. Но для большей простоты и наглядности оставим все как есть.
В качестве примера наполним таблицу следующими значениями:
На этом наша работа с SQL Server закончена, переходим к кодированию проекта.
2. Кодирование пользовательского элемента
Создадим для сего произведения новый проект. Он будет состоять из простой формы с таблицей для тестирования, класса для доступа к данным и собственно пользовательского элемента.
Для доступа к данным из базы создадим свой класс DataSource. Вот его код:
using System; using System.Configuration; using System.Data; using System.Data.SqlClient; namespace Daenur.TabList { /// <summary> /// Summary description for DataSource. /// </summary> public class DataSource { public SqlDataReader GetMenuItems() { // создаем соединение (connectionString берем из Web.config) SqlConnection myConnection = new SqlConnection(ConfigurationSettings.AppSettings[connectionString]); SqlCommand myCommand = new SqlCommand(GetMenuItems, myConnection); myCommand.CommandType = CommandType.StoredProcedure;// и получаем данные myConnection.Open(); SqlDataReader reader = myCommand.ExecuteReader(CommandBehavior.CloseConnection); return reader; } public SqlDataReader GetSubMenuItems(int parentTab) { // создаем соединение (connectionString берем из Web.config) SqlConnection myConnection = new SqlConnection(ConfigurationSettings.AppSettings[connectionString]); SqlCommand myCommand = new SqlCommand(GetSubMenuItems, myConnection); // добавляем параметр - № родительской закладки SqlParameter parameterParentTab = new SqlParameter(@ParentTab, SqlDbType.Int, 4); parameterParentTab.Value = parentTab; myCommand.Parameters.Add(parameterParentTab); myCommand.CommandType = CommandType.StoredProcedure; // и получаем данные myConnection.Open(); SqlDataReader reader = myCommand.ExecuteReader(CommandBehavior.CloseConnection); return reader; } } }
Примечание: для тех, кто не знает, как использовать Web.config для хранения строки соединения, приведу пример:
<appSettings> <!-- строка соединения с базой --> <add key=ConnectionString value=server=localhost;database=TabList;uid=<Ваш_логин>;password=<Ваш_пароль>; /> </appSettings>
Теперь создадим в проекте новый пользовательский элемент управления и назовем его TabList. В файле TabList.ascx напишем следующее:
<%@ Control Language=c# AutoEventWireup=false Codebehind=TabList.ascx.cs Inherits=Daenur.TabList.TabList TargetSchema=http://schemas.microsoft.com/intellisense/ie5%> <table id=menuTable height=* cellSpacing=0 cellPadding=0 width=* border=1 runat=server> </table> <script language=javascript type=text/javascript> var currentSubMenu = null; var currentSubMenuNum = 0; var closeTimer = null; function openSubMenu(num) { var subMenu = document.getElementById(TabList_subMenu + num); if (subMenu != null) { cancelCloseTime(); subMenu.style.display=; if ((currentSubMenu != null) && (currentSubMenuNum != num)) { currentSubMenu.style.display=none; } currentSubMenu = subMenu; currentSubMenuNum = num; } } function closeTime() { closeTimer = window.setTimeout(closeMenu, 1000); } function cancelCloseTime() { if (closeTimer != null) { window.clearTimeout(closeTimer); closeTimer = null; } } function closeMenu() { if (currentSubMenu != null) { currentSubMenu.style.display=none; currentSubMenu = null; currentSubMenuNum = 0; } } document.onclick = closeMenu; </script>
Пока сам элемент это пустая таблица, которая будет наполняться данными из базы и автоматически создавать структуру меню при загрузке страницы. Скрипт здесь нужен для показа или скрытия подменю. При наведении курсора на пункт меню его подменю появляется, а при клике на форме или просто после покидания пункта меню курсором – исчезает.
Файл с кодом (TabList.cs) выглядит так:
namespace Daenur.TabList { using System; using System.Data; using System.Data.SqlClient; using System.Web; using System.Web.UI; using System.Web.UI.HtmlControls; /// <summary> /// Summary description for TabList. /// </summary> // Направление меню (горизонтальное или вертикальное) public enum repeatDirection{Horizontal = 0, Vertical}; public abstract class TabList : System.Web.UI.UserControl { protected System.Web.UI.HtmlControls.HtmlTable menuTable; private DataSource data = new DataSource();// источник данных public repeatDirection RepeatDirection; // направление меню private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here DataBind(); } public override void DataBind() { // получаем все закладки SqlDataReader menuItems = data.GetMenuItems();// если меню горизонтальное if (RepeatDirection == repeatDirection.Horizontal) { // то формируем строку в нашей таблице HtmlTableRow tr = new HtmlTableRow(); tr.ID = menu; tr.Attributes.Add(runat, server); while (menuItems.Read()) { HtmlTableCell td = new HtmlTableCell(); td.ID = menuItem + menuItems[TabOrder]; // создаем код закладки - ссылки // (здесь: onmouseout и onmouseover - события, обработчики которых находятся в скрипте) string HtmlText = @<nobr> <a onmouseout=closeTime() onmouseover=openSubMenu( + menuItems[TabOrder] + @) href= + menuItems[TabOrder] + @> Tab + menuItems[TabOrder] + </a> </nobr>; td.Controls.Add(new LiteralControl(HtmlText)); // для каждой закладки создаем свою таблицу - подменю td.Controls.Add(SubMenu((int) menuItems[TabOrder])); tr.Controls.Add(td); } menuTable.Controls.Add(tr); } else // RepeatDirection.Vertical { // иначе формируем столбец // (все остальное аналогично) while (menuItems.Read()) { HtmlTableRow tr = new HtmlTableRow(); tr.ID = menu; tr.Attributes.Add(runat, server); HtmlTableCell td = new HtmlTableCell(); td.ID = menuItem + menuItems[TabOrder]; string HtmlText = @ <a onmouseout=closeTime() onmouseover=openSubMenu( + menuItems[TabOrder] + @) href= + menuItems[TabOrder] + @> Tab + menuItems[TabOrder] + </a> ; td.Controls.Add(new LiteralControl(HtmlText)); td.Controls.Add(SubMenu((int) menuItems[TabOrder])); tr.Controls.Add(td); menuTable.Controls.Add(tr); } } } /// <summary> /// Функция, создающая подменю для нужной закладки /// </summary> private HtmlTable SubMenu(int tabOrder) { // получаем все закладки для подменю SqlDataReader subMenuItems = data.GetSubMenuItems(tabOrder); // создаем таблицу подменю HtmlTable tbl = new HtmlTable(); tbl.ID = subMenu + tabOrder.ToString(); tbl.Style.Add(display, none);// скрыта по умолчанию tbl.Style.Add(position, absolute);// появляется поверх всего // tbl.Style.Add(filter, alpha (opacity=75)); // прозрачность tbl.Attributes.Add(cellSpacing, 0); tbl.Attributes.Add(cellPadding, 0); tbl.Attributes.Add(width, 160px); tbl.Attributes.Add(border, 1); tbl.Attributes.Add(bgcolor, white); // назначаем обработчики событий tbl.Attributes.Add(onmouseover, cancelCloseTime());// при наведении курсора tbl.Attributes.Add(onmouseout, closeTime());// при выходе за пределы подменю // формируем столбец, состоящий из ссылок while (subMenuItems.Read()) { HtmlTableRow tr = new HtmlTableRow(); HtmlTableCell td = new HtmlTableCell(); td.InnerHtml = @ <a href= + tabOrder + - + subMenuItems[TabOrder] + @> SubTab + tabOrder + - + subMenuItems[TabOrder] + </a> ; tr.Cells.Add(td); tbl.Controls.Add(tr); } return tbl;// возвращаем готовое подменю } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN: This call is required by the ASP.NET Web Form Designer. // InitializeComponent(); base.OnInit(e); } /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } }
Для тестирования нашего меню необходима HTML страница, содержащая наш элемент. Вот HTML код основной формы:
Здесь все просто. Мы создали на форме таблицу и в одну из ее ячеек поместили наш элемент TabList.
Таким образом, получаем само меню в виде таблицы со строкой или столбцом элементов в зависимости от заданного направления и скрытые таблицы – подменю. Видимыми их делает выполнение скрипта при наведении курсора.
Кстати, чтобы поменять направление меню на горизонтальное, достаточно исправить строку в файле Default.aspx:
RepeatDirection=Horizontal на RepeatDirection=Vertical
В результате:
Заключение
Как видите, все довольно просто (а Вы как хотели :)). Я показал, как можно расширить функциональность одностраничного портала, похожего на IBuySpy. Извиняюсь за возможные недочеты. В этом примере показана только суть. Остальное Вы сможете легко реализовать сами. Например, можно прикрутить сюда какую-нибудь графику или просто добавить новые свойства вроде цвета фона. Успехов в изучении новых технологий!