« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга



ПнВтСрЧтПтСбВс
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
    Популярное
Краткий справочник по Transact SQL

Символьный тип

Перспективы Microsoft Silverlight

Пространства имен XML (XML Namespeces)

Файлы, директории и PHP

Функция AccessResource

Приложения ASP.NET

Отладка

SOAP 1.2 и запрос GET

Использование возможностей Shell API




    Архив файлов



    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 142
Пользователей: 0

Статьи:: .NET Framework :: Asp.Net и Web Forms :: Разработка элементов управления ASP.NET на примере навигационной панели



отправить ссылку другу версия для печати  Обсудить на форуме

Разработка элементов управления ASP.NET на примере навигационной панели

Эта проблема стара, как само веб-программирование — даже на самом простом сайте нам нужна панель навигации (или меню). Ну да, та самая, где написано: «О компании», «Услуги», «Прайс-лист», «Сервис» и «Контакты». Давным-давно я писал её на Perl и SSI, потом на PHP, потом на ASP, и конца этому не было, пока не вышла 2-ая версия ASP.NET.



1. Введение

Эта проблема стара, как само веб-программирование — даже на самом простом сайте нам нужна панель навигации (или меню). Ну да, та самая, где написано: «О компании», «Услуги», «Прайс-лист», «Сервис» и «Контакты». Давным-давно я писал её на Perl и SSI, потом на PHP, потом на ASP, и конца этому не было, пока не вышла 2-ая версия ASP.NET.

Разработчики Microsoft предложили удобное расширяемое решение, которое позволило описывать иерархию страниц в несложном XML-файле, а при желании — увязывать между собой структуры из разных источников (например, из базы данных, из дерева каталогов на диске, из нескольких XML-файлов).

Кроме того, мы получили компоненты для отображения структуры сайта: TreeView, Menu и SiteMapPath. Казалось бы — вот оно, счастье!

Однако нет. Я обнаружил, что простым сайтам нужна лёгкая линейная структура, и встроенные компоненты оказываются для этого случая слишком «громоздкими». Вроде всё хорошо, но не совсем понятно, стоит ли для пары-тройки ссылок подгружать столько кода на JavaScript.

С другой стороны, дизайнеры тоже не дремлют — иногда с ними можно договориться о цвете ссылок, но если речь идёт о навигации, они непреклонны. Хорошо, если ребята не настаивают на ручной отрисовке каждого пункта меню, но вот подложку и roll-over им надо сделать обязательно. И объяснить, что TreeView или Menu для этого не предназначены, чертовски сложно.

Единственное, что нам остаётся — написать подходящий компонент самостоятельно. Этим мы и займёмся.

2. Постановка задачи

Для примера возьмём самую простую структуру сайта:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode url="~/Default.aspx" title="Главная страница">
    <siteMapNode url="~/Services.aspx" title="Услуги и цены"/>
    <siteMapNode url="~/Contacts.aspx" title="Контакты"/>
  </siteMapNode>
</siteMap>

Нам бы хотелось получить тривиальную панель навигации, например, вот такого вида:

|  Главная страница  |  Услуги и цены  |  Контакты |

Текущая страница должна выводиться простым текстом, а все остальные — ссылками.

В коде страницы мы хотим задействовать шаблоны (такие же, как и в компоненте Repeater):

<binateq:NavigationPanel ID="NavigationPanel1"
  runat="server" DataSourceID="SiteMapDataSource1">
  <HeaderTemplate>| </HeaderTemplate>
  <ItemTemplate>
    <a href='<%#Container.Url%>'><%#Container.Title%></a>&nbsp;|
  </ItemTemplate>
  <SelectedItemTemplate>
    <span><%#Container.Title%></span>&nbsp;|
  </SelectedItemTemplate>
</binateq:NavigationPanel>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />

Если свести все требования воедино, нужно, чтобы компонент:

  • Умел подключаться к SiteMapDataSource и разворачивал структуру сайта в линейный список.
  • Использовал шаблоны, разные для текущей страницы и для всех остальных.

3. Реализация

Нам «всего лишь» осталось реализовать два перечисленных выше пункта, и затем сделать так, чтобы у нас получился компонент, который можно подвесить на панель инструментов (Toolbox).

3.1. Подключение к SiteMapDataSource

Для того, чтобы наш компонент мог подключиться к SiteMapDataSource, мы должны унаследовать его от класса System.Web.UI.WebControls.HierarchicalDataBoundControl и переопределить виртуальный метод PerformDataBinding:

public class NavigationPanel: HierarchicalDataBoundControl
{
  protected override void PerformDataBinding()
  {
    base.PerformDataBinding();
    nodes.Clear();

    if(!IsBoundUsingDataSourceID && (DataSource == null))
      return;

    HierarchicalDataSourceView view = GetData(string.Empty);

    if(view != null)
    {
      IHierarchicalEnumerable enumerable = view.Select();

      RecurseDataBind(enumerable, 1);
    }
  }
}

Для начала мы должны убедиться, что программист установил одно из свойств DataSource или DataSourceID. Если панель навигации не подключена к SiteMapDataSource (то есть ни одно из свойств не установлено), посетитель сайта увидит содержимое шаблона EmptyTemplate.

Обратите внимание, что DataSource мы проверяем сами, а вот для ревизии DataSourceID необходимо обратиться к свойству IsBoundUsingDataSourceID.

Непосредственный доступ к данным возможен через представление (класс HierarchicalDataSourceView), то есть тем же способом, каким мы работаем с любым источником данных в .NET, будь то SqlDataSource или XmlDataSource.

Метод GetData позволяет получить любое поддерево структуры сайта, но поскольку нам нужна вся иерархия, в качестве пути мы указываем пустую строку.

Всю работу по извлечению данных выполняет наш метод RecurseDataBind, который рекурсивно вызывает сам себя:

private void RecurseDataBind(IHierarchicalEnumerable enumerable, int level)
{
  foreach(object item in enumerable)
  {
    IHierarchyData hierarchyData = enumerable.GetHierarchyData(item);
    SiteMapNode siteMapNode = hierarchyData as SiteMapNode;

    if(siteMapNode != null)
    {
      if(HttpContext.Current == null ||
        siteMapNode.IsAccessibleToUser(HttpContext.Current))
      {
        bool isSelected =
        (siteMapNode.Provider == null) ?
        false :
        (siteMapNode.Provider.CurrentNode == siteMapNode);

        nodes.Add(
          new Node(
            ToAbsolute(siteMapNode.Url),
            siteMapNode.Title,
            siteMapNode.Description,
            level,
            isSelected
          )
        );
      }

      if(hierarchyData.HasChildren)
      {
        IHierarchicalEnumerable recurseEnumerable =
          hierarchyData.GetChildren();

        if(recurseEnumerable != null)
          RecurseDataBind(recurseEnumerable, level + 1);
      }
    }
  }
}

Метод проверяет, что он работает с объектами класса SiteMapNode (программист может передать нашему компоненту любую иерархию, и эту ситуацию надо обрабатывать).

С помощью вызова IsAccessibleToUser мы прячем от неавторизованного посетителя недоступные страницы. Авторизация работает только во время исполнения, когда установлено свойство HttpContext.Current, поэтому при настройке компонента в Visual Studio (design mode) все страницы видимы.

Низкоуровневый доступ к данным осуществляет провайдер (наследник класса SiteMapProvider) из которого мы получаем информацию о текущей странице. Провайдеры доступны только во время выполнения, поэтому в режиме редактирования ни одна страница текущей не является.

Как видим, метод RecurseDataBind сохраняет информацию об уровне вложенности (переменная level), которой мы можем воспользоваться при подготовке шаблонов.

Функция ToAbsolute переводит виртуальные пути (~/Default.aspx) в абсолютные (/Default.aspx). Фактически, она вызывает VirtualPathUtility.ToAbsolute, но кроме того, обрабатывает ситуации, когда в качестве пути заданы полные URI (http://domain.tld/path/filename.ext).

Результатом работы метода RecurseDataBind становится список объектов класса Node, который мы обсудим позднее.

3.2. Шаблоны

Для того, чтобы наш класс понимал шаблоны, мы должны описать свойства класса ITemplate и установить для них несколько атрибутов:

private ITemplate headerTemplate = null;
private ITemplate footerTemplate = null;
private ITemplate itemTemplate = null;
private ITemplate selectedItemTemplate = null;
private ITemplate emptyTemplate = null;

[Browsable(false)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[DefaultValue(typeof(ITemplate), "")]
[TemplateContainer(typeof(Node))]
public virtual ITemplate HeaderTemplate
{
  get { return headerTemplate; }
  set { headerTemplate = value; }
}

В целях экономии места, я опустил описание свойств FooterTemplate, ItemTemplate, SelectedItemTemplate и EmptyTemplate, которые полностью идентичны описанию HeaderTemplate.

С помощью атрибутов мы указываем визуальному редактору (т.е. Visual Studio), как обрабатывать эти свойства:

  • Browsable(false)
    Свойства-шаблоны недоступны на панели Properties во время редактирования. Такое же поведение характерно для «родных» компонентов ASP.NET.
  • PersistentMode(PersistentMode.InnerProperty)
    Шаблоны в коде странице представлены не в виде атрибутов, а в виде вложенных тегов. Пользуясь этой подсказкой, в Visual Studio работает IntelliSense.
  • DefaultValue(typeof(ITemplate), "")
    Значением по умолчанию является пустой шаблон (без текста и вложенных тегов). Этот атрибут позволяет перевести значение свойства в исходное состояние.
  • TemplateContainer(typeof(Node))
    Одно из самых важных свойств, которое обеспечивает привязку к данным (data binding). О подробностях мы поговорим ниже.

Генерация кода страницы выполняется в методе CreateChildControls:

protected override void CreateChildControls()
{
  Controls.Clear();

  if(nodes.Count > 0)
  {
    InstantiateTemplate(headerTemplate);

    foreach(Node node in nodes)
    {
      if(node.IsSelected)
        InstantiateNodeTemplate(selectedItemTemplate, node);
      else
        InstantiateNodeTemplate(itemTemplate, node);
    }

    InstantiateTemplate(footerTemplate);
  }
  else
    InstantiateTemplate(emptyTemplate);
}

Два вспомогательных метода InstantiateTemplate и InstantiateNodeTemplate я написал, чтобы упростить CreateChildControls:

private void InstantiateTemplate(ITemplate template)
{
  if(template != null)
  {
    Control templateHolder = new Control();
    template.InstantiateIn(templateHolder);
    Controls.Add(templateHolder);
  }
}

private void InstantiateNodeTemplate(ITemplate template, Node node)
{
  if(template != null)
  {
    template.InstantiateIn(node);
    Controls.Add(node);
    node.DataBind();
  }
}

Если свойство установлено, нужно добавить в код страницы элементы управления, описанные в шаблоне.

Эту работу выполняет метод InstantiateIn, которому требуется родительский объект Control, где и будут созданы дочерние элементы управления. Если программист определил шаблон в коде страницы, ASP.NET создаёт для нас объект, реализующий интерфейс ITemplate, в том числе и этот метод.

В методе InstantiateNodeTemplate мы пользуемся уже готовым объектом класса Node, который также является наследником Control. Для того чтобы в код шаблона попали значения выражений вида <%#Container.Url%>, мы вызываем метод DataBind.

Давайте подробнее остановимся на том, как происходит связывание с данными (data binding):

  • Данные нужно сначала получить, а затем вставить в Control. Метод InstantiateIn устроен так, что получение данных и отображение выполняется через один и тот же объект, поэтому наш класс Node с одной стороны содержит свойства Url, Title, Description, а с другой — наследует классу Control и используется для отображения шаблона. Такой подход снижает зацепление, т.е. класс выполняет действия, которые никак друг с другом не связаны, и делать так не рекомендуется. Что ж, это тот самый случай, когда мы ничего не можем исправить.
  • Если мы используем внутри шаблона элементы управления, у которых установлен атрибут ID, он будет дублироваться у повторяющихся шаблонов, что приведёт к ошибке. Речь идёт о таких конструкциях, как:
    <ItemTemplate>
     <asp:LinkButton ID=”LBrunat=”serverText=”<%#Container.Title%>” OnClick=”LB_Click/>
    </ItemTemplate>

    Чтобы избежать ошибки, мы должны предупредить ASP.NET, что для дочерних компонентов нужно генерировать уникальные идентификаторы. Для этого класс Node должен наследовать пустому интерфейсу INamingContainer. Поскольку интерфейс пустой (не определяет ни методов, ни свойств), он действует в качестве маркера.
  • Метод DataBind можно вызывать только после того, как шаблон инстанцирован и добавлен в родительский список элементов управления. Извлечение данных идёт вверх по дереву компонентов, поэтому, если бы мы в атрибуте TemplateContainer определили другой тип контейнера, ASP.NET искал бы его среди родителей Node.

Последнее, что мы должны сделать, это увязать между собой разворачивание структуры сайта в линейный список и его отображение:

public override void DataBind()
{
  base.DataBind();
  CreateChildControls();
  ChildControlsCreated = true;
}

Метод base.DataBind среди прочего вызывает PerformDataBinding, и сразу после этого мы создаём дочерние компоненты на базе шаблонов.

3.3. Доводим компонент до ума

3.3.1. Поддержка ViewState и сериализация

Для того чтобы на странице автоматически работали такие компоненты, как GridView и Repeater, ASP.NET вызывает метод DataBind при первой загрузке страницы. Метод вызывается рекурсивно для всех компонентов страницы, они получают данные и сохраняют их в ViewState. При повторных запросах POST данные не считываются до тех пор, пока мы сами этого не сделаем.

Мы должны обеспечить такое же поведение для класса Node, иначе навигационная панель будет «пропадать» со страницы при запросах POST.

Технически, это делается в два этапа:

  • Мы переопределяем методы SaveViewState и LoadViewState у класса NavigationPanel.
  • Мы делаем класс Node сериализуемым.

Код методов SaveViewState и LoadViewState достаточно прост, поэтому я не буду останавливаться на нём подробно:

protected override object SaveViewState()
{
  object[] currentStates = new object[nodes.Count + 1];
  currentStates[0] = base.SaveViewState();

  for(int i = 0; i < nodes.Count; i++)
    currentStates[i + 1] = nodes[i];

  return (object)currentStates;
}

protected override void LoadViewState(object savedState)
{
  if(savedState != null)
  {
    object[] currentStates = (object[])savedState;

    if(currentStates.Length > 0 && currentStates[0] != null)
    {
      base.LoadViewState(currentStates[0]);
      nodes.Clear();

      for(int i = 1; i < currentStates.Length; i++)
      {
        nodes.Add((Node)currentStates[i]);
      }
    }
  }
}

Оба метода предполагают, что объекты класса Node умеют себя сохранять (сериализовывать) и восстанавливать (десериализовывать). В простейших случаях, когда речь идёт о сохранении/восстановлении публичных свойств, достаточно пометить класс атрибутом Serializable, и .NET сам сможет выполнить необходимую работу.

Однако в нашем случае этот способ не подходит, поскольку сериализуемым должен быть не только класс Node, но и все его предки. Проблему в данном случае создаёт класс Control, которому мы должны наследовать.

Чтобы её решить, мы должны реализовать в классе Node интерфейс ISerializable, то есть один-единственный метод GetObjectData:

[SecurityPermission(SecurityAction.LinkDemand, Flags =
SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
  info.AddValue("Url", url);
  info.AddValue("Title", title);
  info.AddValue("Description", description);
  info.AddValue("Level", level);
  info.AddValue("IsSelected", isSelected);
}

Кроме того, мы должны добавить в класс защищённый конструктор, который создаёт объект из сохранённых ранее значений:

protected Node(SerializationInfo info, StreamingContext context)
{
  url = info.GetString("Url");
  title = info.GetString("Title");
  description = info.GetString("Description");
  level = info.GetInt32("Level");
  isSelected = info.GetBoolean("IsSelected");
}

Узлы дерева мы храним в закрытом поле nodes:

private List<Node> nodes = new List<Node>();

3.3.2. Родительский блок DIV

Компонент NavigationPanel является наследником WebControl, который требует, чтобы содержимое компонента размещалось внутри одного из HTML-тегов. По умолчанию в качестве родительского тега используется SPAN, но нам больше подошёл бы тег DIV. Чтобы этого добиться, достаточно переопределить защищённое свойство TagKey:

protected override HtmlTextWriterTag TagKey
{
  get { return HtmlTextWriterTag.Div; }
}

3.3.3. Завершающие штрихи

При описании компонента NavigationPanel мы должны установить несколько атрибутов, чтобы Visual Studio могла правильно работать с ним в коде страницы:

[assembly:TagPrefix("Binateq.Web.Controls", "binateq")]
namespace Binateq.Web.Controls
{
  [AspNetHostingPermission(SecurityAction.Demand, Level =             AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level =
   AspNetHostingPermissionLevel.Minimal)]
  [ParseChildren(true)]
  [ToolboxData(
  "<{0}:NavigationPanel runat="server"> </{0}:NavigationPanel>"
  )]
  public class NavigationPanel: HierarchicalDataBoundControl
  {
  …
  }
}

На слово binateq не обращайте внимания — я употребляю его при написании своего кода, вы же вполне можете задействовать ваше собственное название.

В свойстве assembly:TagPrefix задаётся префикс для компонентов, который будет использован Visual Studio. Обычно она сама генерирует их (uc1, uc2 и т.д.), но такие названия бессмысленны, поэтому я предпочитаю указать префикс при написании компонента.

Атрибут ParseChildren указывает Visual Studio на то, что теги внутри трактуются как значения свойств, то есть содержимое тега HeaderTemplate будет присвоено свойству NavigationPanel.HeaderTemplate.

Атрибут ToolBoxData подсказывает Visual Studio, что именно нужно вставить в код страницы при добавлении компонента. Вместо {0} будет вставлен префикс, в нашем случае binateq.

4. Заключение

Исходный код компонента, вместе с готовой DLL можно скачать по адресу http://mark.shevchenko.name/download/navigationpanel.zip.

Помимо основного файла NavigationPanel.cs вы найдёте там вспомогательный — Utilities.cs, в котором собраны методы для корректного преобразования виртуальных, абсолютных и относительных путей (тот самый метод ToAbsolute, про который я не стал писать в статье).

Можно подключить компонент к панели инструментов Visual Studio. Для этого распахните Toolbox, щёлкните правой кнопкой мыши внутри закладки General и выберите пункт Choose Items. В открывшемся диалоге нажмите кнопку Browse и загрузите Binateq.Controls.dll.

После этого навигационную панель можно будет перетаскивать с панели инструментов в код страницы. Visual Studio автоматически вставит в начало страницы строку <%@ Register Assembly="Binateq.Controls" Namespace="Binateq.Web.Controls" TagPrefix="binateq" %>, и добавит в проект ссылку (reference) на Binateq.Controls.dll.

При выводе структуры сайта мы можем исключить корневую страницу, установив SiteMapDataSource.ShowStartingNode в false. Мы также можем выводить многоуровневую структуру, воспользовавшись свойством Container.Level:

<ItemTemplate>
  <div class="level<%#Container.Level%>">
    <a href='<%#Container.Url%>'><%#Container.Title%></a>
  </div>
</ItemTemplate>
<SelectedItemTemplate>
  <div class="level<%#Container.Level%>">
    <%#Container.Title%>
  </div>
</SelectedItemTemplate>

Определив в таблице стилей классы div.level1, div.level2, div.level3, мы можем с помощью отступов отразить древовидную структуру сайта. Существующий компонент не умеет ограничивать количество уровней вложенности, и за этим придётся следить самостоятельно. Вы можете внести в код компонента необходимые изменения, добавив свойство, например, MaxLevel, и заменив в коде RecourseDataBind

if(hierarchyData.HasChildren)

на
if(hierarchyData.HasChildren && (maxLevel == 0 || maxLevel > level))

Если MaxLevel будет равен 0, то компонент будет показывать все уровни, а если, например, 5, то только первые 5.


Автор: http://markshevchenko.habrahabr.ru/




Рубрика: Asp.Net и Web Forms




Подгрузка через AJAX HTML-кода, содержащег....

AJAX

При разработке CMS S.Builder наша команда активно использовала AJAX. Теперь вот решили поделиться накопленным опытом. Начнем с этого хабратопика. Не буду здесь затрагивать различные фреймворки и библиотеки. Свой код всегда роднее. Для работы с AJAX-ом в S.Builder написана библиотека sbAJAX. Можете качать и пользоваться :). В этом файле есть функция sbEvalJS. Для тех, кто не знает, объясню. При подгрузке через AJAX и вставке на страницу HTML-кода, содержащего JavaScript, JavaScript выполняться не будет или полезут баги. Эта функция как раз решает поставленную задачу.


Подробнее... | Рубрика: AJAX | Добавлено: 19.11.2008

Обзор нового релиза самой мощной Ajax библ....

AJAX

Хотя наш обзор немного запоздал, оригинальный Dojo 1.2 вышел в релизной версии ещё 6-го октября, но сейчас мы наверстаем упущенное. И так, Dojo Toolkit — это самая мощная и гибкая ajax-библиотека из всех, что есть на рынке, она активно развивается и имеет большое комьюнити. Кстати, это самое комьюнити, совместно с компанией Sitepen, имеет ещё несколько проектов, среди которых и Cometd и некоторые другие, не менее интересные, о которых мы скоро вам расскажем. Сегодня же все внимание на флагманский продукт — Dojo 1.2.


Подробнее... | Рубрика: AJAX | Добавлено: 19.11.2008

Firebug 1.3 и 1.4 alpha — что нового и инт....

Вебмастеру

Если вы профессиональный веб-разработчик и постоянно имеете дело с разработкой и отладкой сложных AJAX приложений, то наверняка знаете и используете Firebug — плагин для браузера Firefox, предназначенный для отладки и исследования веб-приложений. Текущая его версия, 1.2х достаточно стабильная и функциональна, чтобы помочь в 99% проблем, которые могут возникнуть при разработке. Но и этот инструмент не лишён если не недостатков, то некоторых фич, которые могли бы облегчить работу. И даже идеальный инструмент можно сделать ещё более идеальным, как бы это не звучало.


Подробнее... | Рубрика: Вебмастеру | Добавлено: 19.11.2008

Остальные статьи:

Релиз Microsoft Silverlight 2.0. Что новог...
XML документация в C#
Курсоры в MySQL 5
Microsoft опубликовала подробности о сесси...
Microsoft делится подробностями о том, что...
Тестируем новый javascript от нового брауз...
MySQL Query Cache
Использование провайдеров компиляции в As...
Чего мы ждем от C# 4.0
Delphi 2009 и C++Builder 2009
Джоэл Спольски и Джеф Этвуд запустили новы...
Поиск кода Google /* что нового? */
10 jQuery скриптов для улучшения интерфейс...
Генераторы отчетов FastReport 4 и QuickRep...
День программиста — набор стерeотипов
Индусские програмисты
Вышел Django 1.0
Портативная версия Google Chrome Portable
Исходные коды .Net Frameword 3.5 SP1 для о...
Пишем правильный online WYSIWYG-редактор


Цитата дня (все,добавить):

Портал фрилансеров

работа на дому


    Рубрикатор

Программирование

C/С++
Обучение
Windows API
XAML
Моделирование
Паттерны
Visual Basic 7 .NET
WxWidgets
Функции WinApi
Функции С++
Разработка под Mac OS
Eiffel
Visual Studio 2008
UI дизайн
Алгоритмы
Конкурсные статьи
Turbo Pascal
Visual Studio
CASE-средства
Visual Studio 2005
Без VCL
Delphi
Тех. документация
Тестирование
Software Testing
ООП
TCP/IP
Google Android
Windows Installer
.NET Framework
Драйвера
C# C Sharp
Справка
Проектирование
Информ. системы
Visual Basic
Assembler
Оптимизация кода
Gtk+
Компоненты
Реинжиниринг
Управление проектами
Extreeme programming
Lotus Notes
Алгебраическое проектирование


Интернет технологии

PHP
Perl
ASP
WAP
Cookies
SSI
CGI
Web Servers
VB Script
DNS
CSS
XML
Html
Java Script
Java2ME
Firewall
Flash
.htaccess
Apache
VRML
Протоколы
Поисковые системы
Технология JAVA
Учебник по PHP
Учебник по JavaScript
Учебник по XML
Java Q&A
AJAX
DHTML
XHTML
Dreamweaver
Web 2.0
Python
Вебмастеру
Cisco
Ruby on Rails
Silverlight

Базы данных

Access
InterBase
MySQL
Oracle
ADO .NET
Основы SQL
Учебник по Access 2002
MS
Microsoft FoxPro
Доступ к данным
XML в MS SQL Server 2000
ODBC и MyODBC
Обучение
Caché
DB2
PostgresSQL
Sybase
Теория
Хранилища данных
Безопасность
Реляционные данные
MySQL и mSQL

Остальное:

Разное
Обзоры книг
Безопасность
Графика и дизайн
Юмор
Linux
Фракталы
Microsoft Axapta
Многоядерность
Сети
Microsoft Office
Работа
MS-DOS
Криптография
Графика и игроделание
Новости SDK
Системы защиты
Учебник по AutoCad
CVS
Windows XP
Windows Server 2003
Windows Vista
Windows 7
Мероприятия