В статье предлагается единый набор правил для грамотной/поддерживаемой/расширяемой организации архитектуры web-контролов с использованием платформы FlexberryASP.NET.

Подключение скриптов и стилей

  1. Контрол должен быть максимально автономен с точки зрения зависимостей от скриптов/стилей. Должна быть возможность подключить контрол на пустой ASP.NET странице (System.Web.UI.Page) без дополнительных манипуляций.
    • все используемые JS-библиотеки и скрипты должны подключаться самим контролом;
    • если контрол использует общие JS-библиотеки (например, jQuery), то все зависимые скрипты должны подключать после этапа Init (Load и далее) т.к. базовые библиотеки подключаются в MasterPage.Init; подключение на этапе Init приведет к тому, что требуемая библиотека будет подключаться позже в разметке со всеми вытекающими последствиями;
  2. Все скрипты и стили должны храниться в веб-ресурсах сборки.
  3. Подключать все скипты и стили следует при помощи PageContentManager в OnLoad.
    • это предотвращает множественное подключение одних и тех же скриптов/стилей, если файл поддключается в нескольких местах (контролах);

Инициализация внутренней структуры контрола

Загрузка данных

  1. Все данные должны быть полностью загружены к концу этапа Load

Работа с идентификаторами

Особенности генерации клиентских идентификаторов контролов в ASP.NET:

  1. Начиная с .NET Framework 4, для генерации ClientId используются несколько алгоритмов - т.н. ClientIDMode (MSDN).
  2. Если у контрола не указан ID (ID == null), то он будет автоматически сгенерирован при первом обращении к ClientID используя текущий ClientIDMode.
  3. Если у контрола указан id в атрибутах, и указан ID, то при рендеринге будет использован CleintID. Если ID не указан, то будет использовано значение из атрибута.

В связи с этим может появляться странное поведение при генерации идентификаторов в отладичке, например, при использовании Watch.

Пример:

_inputField = new HtmlGenericControl("div"); // ID не задан => _inputField.ID == null
_inputField.Attributes["id") = "SomeID";     // Клиентский идентификатор задан через HTML атрибут.
                                             // Если где-то до рендеринга / в Watch будет вызван _inputField.ClinetID,
                                             // то ID != null => будет использован сгенерированный CleintID.

Ожидаемый результат рендеринга:

<div id="SomeID"></div>

Полученный результат рендеринга (при обращении к ClientID):

<div id="ctl90"></div>