Игорь Чакрыгин Игорь Чакрыгин

У любой задачи существует по крайней мере одно очевидное и невероятно простое для понимания неправильное решение

ASP.NET - Подключение пользовательских элементов управления без директивы Register

В ASP.NET перед тем как использовать пользовательский элемент управления (User Control) на странице, его необходимо подключить при помощи директивы Register.

В большинстве случаев, использование этой директивы излишне, поскольку пользовательские элементы управления часто используются на странице только один раз. При этом, чтобы вспомнить их месторасположение нужно сначала найти соответствующую директиву Register.

В этой статье я бы хотел рассказать о небольшом трюке, который позволяет использовать пользовательские элементы управления без директивы Register.

Пример

Для примера рассмотрим элемент управления Hello.ascx.

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

Я хочу, чтобы директиву Register можно было убрать, а путь указать прямо в теге.

Тип элемента управления при этом должен остаться таким же. Это означает, что все свойства (в данном случае свойство Name) и методы должны быть доступны в коде как и прежде.

Решение

Для начала, в папке App_Code (можно вынести и в отдельную сборку) создадим файл IncludeAscx.cs c элементом управления IncludeAscx.

Он будет является своеобразной заглушкой, т.к. будет объявляться на странице как любой другой элемент управления, но создаваться вместо него будет элемент управления другого, нужного нам типа.

Для того, чтобы его можно было объявить на странице зарегистрируем его в файле Web.Config.

Для того, чтобы в момент компиляции изменить тип создаваемого элемента управления используем построитель элемента управления (Control Builder).

Создадим в папке App_Code файл IncludeAscxControlBuilder.cs с построителем элемента управления IncludeAscxControlBuilder.

Переопределим метод Init, который вызывается всякий раз, когда парсер страницы встречает тег серверного элемента управления (В нашем случае тег <asp:IncludeAscx runat="server" />). Из всех параметров данного метода интерес представляют только параметры parser, type и attribs.

Параметр attribs позволяет получить значения атрибутов тега, размещённого на странице. При помощи этого параметра мы получим значение атрибута AscxPath.

Параметр type определяет тип элемента управления, который будет создан. Значение этого параметра необходимо изменить до вызова базовой реализации метода Init.

Параметр parser может иметь один из трёх типов: PageParser, UserControlParser или MasterPageParser. Все они наследуются от класса BaseTemplateParser, который имеет метод GetUserControlType. Этот метод принимает абсолютный или относительный виртуальный путь, компилирует пользовательский элемент управления, который расположен по этому пути, добавляет ссылку на скомпилированную сборку и возвращает тип скомпилированного элемента управления, который мы и сохраняем в параметре type.

Стоит отметить, что построители элементов управления предоставляют широчайшие возможности для кодогенерации, однако все они тщательно скрыты от разработчика и доступ к ним можно получить только при помощи рефлексии. Увы, метод GetUserControlType не является исключением.

Всё готово. Теперь мы можем добавлять пользовательские элементы управления на страницу при помощи элемента управления IncludeAscx, как было показано выше.

Ложка дёгтя

Как я уже сказал выше, построители элементов управления предоставляют широчайшие возможности для кодогенерации. Редактор кода, который встроен в Visual Studio, поддерживает их в полной мере. Это означает, что любые изменения, которые выполняет построитель отражаются на подсветке синтаксиса и механизме IntelliSense.

В этом примере поле Hello будет иметь нужный нам тип и мы будем иметь доступ к его свойству Name, как будто элемент управления добавлен на страницу при помощи директивы Register.

К сожалению, вы будете лишены всего этого, если вы привыкли пользоваться ReSharper`ом. Он не умеет работать с построителями элементов управления и будет считать, что поле Hello имеет тип IncludeAscx и не имеет никакого свойства Name, хотя проект при этом будет нормально компилироваться.

Скачать демо (ASP.NET 4.0, Web Site)

4 комментария

  1. А еще оно будет работать только в Web Site, но не в Web Application Project.

    ОтветитьУдалить
  2. Ещё как будет работать!
    Просто элемент управления IncludeAscx нужно вынести в отдельную сборку.

    Это связано с тем, что Control Builder участвует в процессе компиляции, а Web Application компилируется сразу, поэтому Control Builder нельзя поместить в тот же Web Application.

    ОтветитьУдалить
  3. А почему нельзя просто в веб конфиге


    далее просто контролы помещать в namespace Myapps.Controls.

    ОтветитьУдалить
    Ответы
    1. Во-первых, поместить контролы в namespace можно только в Web Application. В Web Site так не получится.

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

      В-третьих, способ можно как-нибудь расширить. Например, регистрировать контролы не по пути или типу, а по названию, как-то так:
      <asp:Include runat="server" ControlName="LoginForm" />

      Т.е. полностью абстрагироваться от того, где расположен сам контрол.

      В-четвёртых, ну потому что это можно сделать =) Большей частью это был эксперимент =)

      Удалить

© Игорь Чакрыгин. Все права защищены при помощи чёрной магии. Технологии Blogger.