Динамическое добавление пользовательского элемента управления в ретранслятор - PullRequest
5 голосов
/ 06 октября 2011

У меня есть класс (MyClass), который представляет вложенную иерархию, поэтому у класса есть свойство, которое является коллекцией MyClass. MyClass также имеет свойство title

Чтобы показать его на веб-странице, я надеялся создать пользовательский элемент управления с ретранслятором. В шаблоне элемента у меня будет литерал для отображения свойства title, а в событии ItemCreated повторителя я создам новый экземпляр usercontrol и добавлю его в текущий элемент в повторителе.

Моя проблема заключается в том, что при возникновении события Page_Load в пользовательском контроле, если элемент управления был добавлен динамически, репортер repMyClass poroperty имеет значение null, даже если я вызываю EnsureChildControls. Я что-то здесь упускаю? Если я создаю повторитель и добавляю свой userctonrol в шаблон элемента, он работает нормально. Я не могу заставить это работать динамически?

Контроль пользователя:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyControl.ascx.cs" Inherits="MyControl" %>
Items:<br/>
<asp:Repeater ID="repMyClass" runat="server" EnableViewState="false" 
    OnItemCreated="repMenuItems_ItemCreated">
    <HeaderTemplate><ul><HeaderTemplate>
    <ItemTemplate>
        <li><%# Eval("Title") %>
            <div><asp:PlaceHolder ID="SubItemPlaceholder" runat="server" /></div>
        </li></ItemTemplate>
    <FooterTemplate></ul></FooterTemplate>
</asp:Repeater>

Код контроля пользователя:

public partial class MyControl: System.Web.UI.UserControl
{
    public IEnumerable<MyClass> ChildItems { get; set; }
    protected void Page_Load(object sender, EventArgs e)
    {
        this.repMyClass.DataSource = ChildItems;
        this.repMyClass.DataBind();
    }

    protected void repMenuItems_ItemCreated(object Sender, RepeaterItemEventArgs  e)
    {
        if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
        {
            //Get the MyClass instance for this repeater item
            MyClass mcCurrent = (MyClass)e.Item.DataItem;

            //If the MyClass instance has child instances
            if (mcCurrent.Children != null && mcCurrent.Children.Length > 0)
            {
                //Add a new user control and set it's property so it can bind
                PlaceHolder ph = (PlaceHolder)e.Item.FindControl("SubItemPlaceholder");

                MyControl ctl = (MyControl)Page.LoadControl(typeof(MyControl),new object[] {});

                ctl.ChildItems = mcCurrent.Children;
                ph.Controls.Add(ctl);
            }
        }
    }
}

Ответы [ 2 ]

3 голосов
/ 07 октября 2011

Я уже давно это делал для создания вложенных отчетов с использованием Accordions.

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

// Declare Placeholder    
PlaceHolder ph = (PlaceHolder)e.Item.FindControl("SubItemPlaceholder")

// Adding some literal controls to header
ph.Controls.Add(new LiteralControl("This is the accordion header!!"));

// Declare new control variable
crt = new Control();

// Load up your User Control
crt = LoadControl("~/MyControl.ascx");

// Check if it has loaded properly
if (crt != null)
{
    // GET / SET any custom properties of the User Control
    ((myClass)crt).title = "Welcome";

    // Add the new User Control to the placeholder's controls collection
    ph.Controls.Add(crt);
}

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

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="myTest" ClassName="myClass" %>

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

public string title { get; set; }

Так что, если вы хотите принудительно установить значение при создании для «repMyClass», вы можете установить его как свойство и назначить любое значение, которое вы хотите, программным путем.

0 голосов
/ 07 октября 2011

Вы можете создать другой конструктор в элементе управления, который получит требуемое свойство, использовать его в событии load, а затем передать его в текущий пустой массив объектов в вызове LoadControl ().

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

Другой подход - не загружать его динамически, как все.

внутри шаблона элемента вы можете иметь что-то вроде:

<uc:MyControl runat="server 
    ChildItems='<%# ((MyClass) Container.DataItem).Children %>'
    Visible='<%# ((MyClass) Container.DataItem).Children.Length > 0 %>'
    />

Обновление

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

На самом деле, когда я думаю об этом сейчас, ваш код не должен этого делать, потому что e.Item.DataItem еще не был связан с ним.

Попробуйте изменить событие на ItemDataBound и посмотреть, работает ли оно.

.

Я все же рекомендую вам включить элемент управления в разметку элемента и контролировать видимость элемента управления в событии (теперь это ItemDataBound). Вы можете ссылаться на элемент управления, например, e.Item.FindControl("ControlId") as MyControl (вернет ноль, если не найден в текущем элементе).

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

...