пʼятниця, 30 травня 2008 р.

Юзер контроли на ASP.Net

Сьогодні на роботі написав свій перший повноцінний юзер-контрол на ASP.Net - TabControl (вкладки). Наразі, щоправда, без AJAX - так сказало начальство :) На диво, в АСП версії 2.0 немає такого цінного і потрібного контрола, а значить його треба писати руцями.
Спочатку в двох словах опишу ідею, яку я нарив в гуглі: таб контрол пишеться за допомогою трьох стандартних контролів: Menu, MultiView i View. Перший буде виконувати роль кнопочок вверху вкладок, два інші - все, що внизу.
Почнемо з малого - опишемо клас TabView:
using System.Web.UI.WebControls;

namespace MyControlsLib.TabControl
{
public class TabView : View
{
public string TabName { get; set; }
}
}

Як бачимо, він всього лише розширює функціональність стандартного View, додаючи йому пропертю TabName - заголовок вкладки. Все =)
Тепер трохи складніше - опишемо клас TabMultiView, який і буде контейнером для наших TabView:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyControlsLib.TabControl
{
[ParseChildren(true, "Views")]
public class TabMultiView : CompositeControl
{
public TabMultiView()
{
m_MultiView = new MultiView();
m_Menu = new Menu();
}

protected override void CreateChildControls()
{
base.CreateChildControls();

m_Menu.MenuItemClick += Menu_Click;
int index = 0;
foreach (var tab in Views)
{
MenuItem item = new MenuItem(tab.TabName, index.ToString());
item.Selected = index == ActiveIndex;
m_Menu.Items.Add(item);

m_MultiView.Views.Add(tab);

index++;
}

m_Menu.Orientation = Orientation.Horizontal;

if(!string.IsNullOrEmpty(SelectedTabCssClass))
{
m_Menu.StaticSelectedStyle.CssClass = SelectedTabCssClass;
m_Menu.StaticSelectedStyle.HorizontalPadding = 0;
m_Menu.StaticSelectedStyle.VerticalPadding = 0;
}
else
{
m_Menu.StaticSelectedStyle.BackColor = Color.LightGray;
m_Menu.StaticSelectedStyle.ForeColor = Color.Black;
}

if(!string.IsNullOrEmpty(UnselectedTabCssClass))
{
m_Menu.StaticMenuItemStyle.CssClass = UnselectedTabCssClass;
m_Menu.StaticMenuItemStyle.HorizontalPadding = 0;
m_Menu.StaticMenuItemStyle.VerticalPadding = 0;
}
else
{
m_Menu.StaticMenuItemStyle.BackColor = Color.Black;
m_Menu.StaticMenuItemStyle.ForeColor = Color.White;
}

// multiview

Controls.Add(m_Menu);
Controls.Add(m_MultiView);
}

private void Menu_Click(object sender, MenuEventArgs e)
{
ActiveIndex = int.Parse(e.Item.Value);
}

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if(ActiveIndex < 0)
{
if(Views.Count > 0)
{
throw new NotSupportedException("Invalid ActiveIndex property. You must set it correct.");
}
}
else
{
m_MultiView.Visible = true;
}
}

public string UnselectedTabCssClass { get; set; }
public string SelectedTabCssClass { get; set; }

public int ActiveIndex
{
get
{
object viewState = ViewState["ActiveIndex"];
if(viewState == null)
{
return -1;
}
return (int) viewState;
}
set
{
ViewState["ActiveIndex"] = value;
m_MultiView.ActiveViewIndex = value;
}
}

public List<TabView> Views
{
get
{
if (m_Tabs == null)
{
m_Tabs = new List<TabView>();
}
return m_Tabs;
}
}

private Menu m_Menu;
private MultiView m_MultiView;
private List<TabView> m_Tabs;
}
}


Тепер коротенько, що, де, коли і навіщо:
1. Наслідуємося від CompositeControl - це такий контрол, який дозволяє тримати всередині інші контроли - у нас же всередині будуть лежати TabView та менюшка.
2. Вписуємо в нього пропертю List<TabView> Views - отут якраз вони і будуть лежати. Вішаємо на клас атрибутик [ParseChildren(true, "Views")] - цим самим ми кажемо компілятору, що всі внутрішні контроли треба пхати в ліст "Views".
3. Пропертя int ActiveIndex - індекс активної вкладки. Значення тримаємо в ViewState - його потрібно зберігати між перезавантаженнями сторінки.
4. Перевантажуємо метод CreateChildControls() - в ньому потрібно створити контроли, з яких і буде складатися наш мега-контрол. Тут пробігаємося по Views - там уже сидять TabView, які ми вписали всередину контрола і витягуємо з них імена для елементів меню. Для кожного елемента потрібно запам'ятати в проперті Value його індекс - щоб знати, який TabView активувати при кліку на елементі. Крім того додаємо в m_MultiView таби. В кінці не забуваємо додати меню і мультів'ю в список контролів і обробити клік по пункту меню: m_Menu.MenuItemClick += Menu_Click;.
5. Додаємо купу фігні, яка відповідає за красивості з CSS-стилями.
Дописуємо в Web.config пару рядочків для зручності користування контролами:
<pages>
<controls>
<add assembly="MyControlsLib" namespace="MyControlsLib.TabControl" tagPrefix="my"/>
</controls>
</pages>

Все. Тепер можна користуватися:
<my:TabMultiView runat="server" ActiveIndex="0" SelectedTabCssClass="SelectedTab" UnselectedTabCssClass="UnselectedTab">
<my:TabView runat="server" TabName="Tab1">
<asp:Panel runat="server" CssClass="TabPanel">
Blah-Blah-Blah
</asp:Panel>
</my:TabView>
<my:TabView runat="server" TabName="Second tab">
<asp:Panel runat="server" CssClass="TabPanel">
Мне бы джина, я б тогда не путался в проводах<br />
Прям из Крыма по Днепру бы подтянул Карадаг<br />
До зарплаты за неделю не был бы на мели<br />
Королевы и принцессы in love with me, yeah.<br />
Тру бутылки, банки, кружки, баночки тоже тру<br />
Спрячу джина под подушку, достану поутру<br />
Вот тогда бы мы дел наделали, вот тогда б зажгли<br />
Все дела поотменять, потому что у меня...<br /><br />
На восьмом этаже - пати в неглиже<br />
На седьмом этаже - соседи спят уже<br />
На девятом этаже - медведи и Фаберже<br />
Этажом выше - спецназ на крыше.<br />

</asp:Panel>
</my:TabView>
<my:TabView runat="server" TabName="Yet another tab...">
<asp:Panel runat="server" CssClass="TabPanel">
Hello world!
</asp:Panel>
</my:TabView>
</my:TabMultiView>

Після легкої настройки CSS можна отримати таке:

Я думаю, будь-кому зрозуміло, що прикрашати контрол можна й далі - але це вже виходить за рамки цього меседжа.

четвер, 29 травня 2008 р.

Hello world!

#include < iostream >

int main()
{
std::cout << "Hello world!";
return 0;
}