Как создаются пользовательские элементы управления в asp.net? - PullRequest
1 голос
/ 16 декабря 2011

Насколько я понимаю, когда компонент или пользовательский элемент управления определяется на странице aspx с помощью тега <%Register%>, он объявляется компилятором в автоматически создаваемый файл designer.cs (C #).Если этот пользовательский элемент управления никогда не используется на странице aspx, происходит ли это по-прежнему в файле designer.cs?

Если предполагается, что элемент управления используется на странице aspx, какой механизм затем создает этот элемент управления, как он новый?за кулисами?Файл конструктора только декларирует это.Большое спасибо, и если есть хорошие статьи, обсуждающие это, я буду рад их прочитать.

1 Ответ

1 голос
/ 16 декабря 2011

Это может ответить не на все ваши вопросы, но на некоторые из них. Рик Страл недавно написал отличную статью о компиляции и развертывании, которая описывает, как она работает:

Компиляция и развертывание в ASP.NET 2.0

Я добавил часть статьи, которая, я думаю, больше всего относится к вашему вопросу ниже:

Ссылки на другие страницы и элементы управления

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

Если вам нужно сослаться на другую страницу из элемента управления или другой страницы, вам нужно явно импортировать ее с помощью директивы @Reference. Опять же, это отличается от ASP.NET 1.1, где все классы CodeBehind были немедленно доступны для всего вашего веб-приложения. В ASP.NET 2.0 для загрузки требуется явная ссылка на сборку.

Предположим на минуту, что у вас есть страница DataEntry.aspx, которую я показывал ранее, и вы хотите создать вторую страницу, использующую тот же класс CodeBeside, чтобы вы могли повторно использовать логику страницы, но изменили макет страницы в DataEntry2.aspx с помощью изменив несколько цветов и перемещаясь по элементам управления страницы. По сути, вы хотите, чтобы две страницы ASPX ссылались на один и тот же файл CodeBeside.

Вот как это сделать:

<%@ Reference Page="~/DataEntry.aspx" %>    
<%@ Page Language="C#" AutoEventWireup="true" Inherits="DataEntry" %> 

Я опускаю атрибут CodeFile, ссылающийся на класс CodeBeside страницы DataEntry, и добавляю тег @Reference на страницу, чтобы принудительно импортировать класс CodeBeside.

То же самое верно для любых определений пользовательских элементов управления. Чтобы импортировать пользовательский элемент управления, необходимо использовать тег @Register, который импортирует сборку, в которой находится этот элемент управления. ASP.NET умен во время компиляции и точно определяет, где находятся связанные сборки, основываясь на том, как компилируется проект. Если элемент управления или страница находятся в одной сборке, ссылка фактически не добавляется. Но если он внешний - например, в другом каталоге, то добавляется ссылка на сборку.

Проблемы со ссылками

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

Самая распространенная проблема, с которой я сталкиваюсь, - это динамическая загрузка элементов управления. В ASP.NET 1.x вы можете запустить такой код для динамической загрузки элементов управления на страницу:

public partial class DynamicControlLoading : System.Web.UI.Page    
{    
    protected CustomUserControl MessageDisplay = null;  

    protected void Page_Load(object sender, EventArgs e)    
    {    
        MessageDisplay = this.LoadControl( "~/UserControls/CustomUserControl.ascx")   as CustomUserControl;

        this.Controls.Add(MessageDisplay);

    }

    protected void btnSay_Click(object sender, EventArgs e)    
    {    
        this.MessageDisplay.ShowMessage(this.txtMessage.Text);

    }

}

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

Если вы запустите приведенный выше код в ASP.NET 2.0, он, скорее всего, потерпит неудачу. Я говорю «вероятно», потому что есть некоторые несоответствия, которые иногда автоматически выбирают ссылки на элементы управления, например, если пользовательский элемент управления находится в том же каталоге и компилируется в ту же сборку, что и страница, или если на другую страницу ссылается элемент управления.

Должен и обычно терпит неудачу. Зачем? Потому что ASP.NET компилируется на уровне каталога, а CustomUserControl находится в отдельном каталоге и, следовательно, переходит в отдельную сборку. Классу страницы не видно, чтобы получить строго типизированную ссылку. Intellisense покажет большой, толстый и красный восклицательный знак или вообще ничего для элемента управления MessageDisplay. Когда вы запустите страницу, она будет бомбить.

YoВы, конечно, можете ссылаться на элемент управления как тип элемента управления, но если вам нужно получить доступ к каким-либо пользовательским свойствам в пользовательском элементе управления, кроме свойств элемента управления, вы не сможете, если не прибегаете к Reflection.Насколько я знаю, нет способа добавить ссылку на другой пользовательский элемент управления или страницу программно, потому что ссылка должна быть доступна намного раньше, во время компиляции, прежде чем ваш код когда-либо будет выполняться.

Альтернативы - не загружать элементы управления динамическиили, по крайней мере, предоставьте некоторый механизм для предварительной загрузки любых пользовательских элементов управления на странице с соответствующими тегами @Register.Но это не всегда возможно.Другой вариант - создать базовый класс управления пользователем в APP_CODE и открыть там открытый интерфейс.Основная проблема заключается в том, что этот базовый класс не будет иметь доступа ни к каким внутренним элементам управления пользовательского элемента управления, и поэтому базовый класс должен будет использовать FindControl для ссылки на любые встроенные элементы управления.Так что это чертовски неэффективно и громоздко для загрузки.

Я сталкивался с подобными ситуациями со сценариями наследования.Например, наследование одной главной страницы от другого класса CodeBeside.Все работает хорошо, но компилятор ASP.NET жалуется, что объект Profile незаконно переопределяется (предупреждение компилятора).Работа с унаследованной главной страницей работает, но есть свои причуды.Пользовательские элементы управления, добавленные на главную страницу, часто заканчиваются конфликтами типов, поскольку ASP.NET рассматривает пользовательский элемент управления, добавленный на базовую страницу, как тип, отличающийся от пользовательского элемента управления, добавленного на вторую страницу.

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

Итог: общая модель компиляции ASP.NET 2.0 внутренне сложна.Большую часть времени вам не нужно это понимать, но когда вы сталкиваетесь с этими граничными сценариями, вы действительно ДОЛЖНЫ понимать, что происходит за кулисами, чтобы иметь возможность обходить причуды.

...