![]() |
Всплывающее меню для одностраничного портала (исходники)Источник: hardline Золотухин Роман
Недавно решил изучить 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 код основной формы: <%@ Register TagPrefix="TabListControl" TagName="TabList" Src="TabList.ascx" %>
<%@ Page language="c#" Codebehind="Default.aspx.cs" AutoEventWireup="false" Inherits="Daenur.TabList.DefaultForm" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>DefaultForm</title>
</HEAD>
<body MS_POSITIONING="GridLayout">
<TABLE height="523" cellSpacing="0" cellPadding="0" width="174" border="0" ms_2d_layout="TRUE">
<TR vAlign="top">
<TD width="174" height="523">
<form id="DefaultForm" name="DefaultForm" action="Default.aspx" method="post">
<TABLE height="157" cellSpacing="0" cellPadding="0" width="511" border="0" ms_2d_layout="TRUE">
<TR vAlign="top">
<TD width="10" height="15"></TD>
<TD width="501"></TD>
</TR>
<TR vAlign="top">
<TD height="142"></TD>
<TD>
<table height="141" cellSpacing="0" cellPadding="0" width="500">
<tr>
<td width="20"></td>
<td width="*">
<TABLISTCONTROL:TABLIST id="TabList" runat="server" RepeatDirection="Horizontal"></TABLISTCONTROL:TABLIST>
</td>
<td width="20"></td>
</tr>
<tr>
<td width="20"></td>
<td width="*"></td>
<td width="20">
</td>
</tr>
</table>
</TD>
</TR>
</TABLE>
</form>
</TD>
</TR>
</TABLE>
</body>
</HTML>
Здесь все просто. Мы создали на форме таблицу и в одну из ее ячеек поместили наш элемент TabList. Таким образом, получаем само меню в виде таблицы со строкой или столбцом элементов в зависимости от заданного направления и скрытые таблицы - подменю. Видимыми их делает выполнение скрипта при наведении курсора. Кстати, чтобы поменять направление меню на горизонтальное, достаточно исправить строку в файле Default.aspx: RepeatDirection="Horizontal" на RepeatDirection="Vertical" В результате: ![]() Заключение Как видите, все довольно просто (а Вы как хотели :)). Я показал, как можно расширить функциональность одностраничного портала, похожего на IBuySpy. Извиняюсь за возможные недочеты. В этом примере показана только суть. Остальное Вы сможете легко реализовать сами. Например, можно прикрутить сюда какую-нибудь графику или просто добавить новые свойства вроде цвета фона. Успехов в изучении новых технологий! |