Почему дети моего пользовательского элемента управления не инициализируются? - PullRequest
5 голосов
/ 09 января 2009

ОБНОВЛЕНИЕ: Я немного поэкспериментировал со своими элементами управления и думаю, что стал ближе. Пожалуйста, ознакомьтесь с обновленной информацией.

У меня есть 2 пользовательских элемента управления ASP.NET 2.0, один из которых размещен внутри другого. Внутри внутреннего элемента управления у меня есть элемент управления тегом HtmlAnchor, для которого я пытаюсь задать URL-адрес из внешнего элемента управления. Когда я пытаюсь установить свойство HRef из разметки страницы рендеринга, элемент управления HtmlAnchor имеет значение null и выдает исключение.

Свойство URL set вызывается до события OnInit внутреннего или внешнего элемента управления и до события 'OnPreInit' главной страницы. Я предполагаю, что это потому, что я устанавливаю URL дочернего элемента управления в разметке родительского элемента управления на странице, на которой я отображаю элементы управления, что означает, что значение установлено до OnInit(). Вот (урезанная) версия кода, который я использую:

[ParseChildren(true)]// <- Setting this to false makes the 
                     //    page render without an exception 
                     //    but ignores anything in the markup.
[PersistChildren(false)]
public partial class OuterControl : System.Web.UI.UserControl
{
    // The private '_Inner' control is declared 
    // in the .ascx markup of the control
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public InnerControl Inner
    {
        get{ return _Inner; }
        set{ _Inner = value; }
    }
}

public partial class InnerControl : System.Web.UI.UserControl
{
    // The private 'linkHref' control is declared
    // in the .ascx markup of the control
    public string Url
    {
        get { return linkHref.HRef; }
        set { linkHref.HRef = value; }
    }
}

OuterControl используется на моей странице Default.aspx следующим образом:

<uc1:OuterControl ID="OuterCtrl1" runat="server">
    <Inner Url="#" />
</uc1:OuterControl>

В приведенном выше примере разметки, если я пытаюсь отобразить эту страницу, возникает исключение, поскольку элемент управления linkHref имеет значение null. Используя мой отладчик, я вижу, что каждый элемент управления в InnerControl имеет значение null, но событие OnInit() InnerControl и OuterControl еще не было запущено при обращении к свойству Url.

UPDATE
Я думал, что добавление атрибутов 'ParseChildren' и 'PersistChildren' поможет. Я использовал их в элементах управления сервером раньше, но никогда в пользовательских элементах управления, хотя эффект, похоже, похож. Я не думаю, что правильно интерпретирую документацию по этим двум свойствам, но это может помешать выбросу исключений. Однако разметка страницы игнорируется.

Кто-нибудь знает способ получить эту работу. Я не понимаю, почему эти элементы управления получают значения, установленные до OnInit(). Когда я пытаюсь установить их значения с помощью разметки ASPX, конструктор для InnerControl вызывается дважды. Один раз, чтобы установить значения, основанные на разметке (я предполагаю), и снова на OnInit() (что я предполагаю, почему значения разметки игнорируются).

Это усилие безнадежное или я просто подхожу к нему с неправильного угла?

Ответы [ 5 ]

2 голосов
/ 17 мая 2009

Привет Дэн,

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

Я воспроизвел проблему следующим образом: -

Создан пользовательский элемент управления с именем Inner:

Inner.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Inner.ascx.cs" Inherits="Inner" %>
<a runat="server" id="linkHref">I'm inner</a>

Inner.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Inner : System.Web.UI.UserControl
{
    public string Url
    {
        get
        {
            return this.linkHref.HRef;
        }
        set
        {
            this.linkHref.HRef = value;
        }
    }
}

Создан пользовательский элемент управления с именем Outer:

Outer.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Outer.ascx.cs" Inherits="Outer" %>
<%@ Register src="Inner.ascx" tagname="Inner" tagprefix="uc1" %>
<uc1:Inner ID="_Inner" runat="server" />

Outer.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Outer : System.Web.UI.UserControl
{
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public Inner Inner
    {
        get
        {
            return this._Inner;
        }
    }
}

Затем создал страницу с именем По умолчанию:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register src="Outer.ascx" tagname="Outer" tagprefix="uc1" %>
<%@ Reference Control="~/Inner.ascx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <uc1:Outer ID="Outer1" runat="server">
            <Inner Url="http://google.com" />
        </uc1:Outer>
    </div>
    </form>
</body>
</html>

При всем этом страница «по умолчанию» работает хорошо.

То, что я нахожу странным в вашем коде, это строка:

public InnerControl Inner
    {
        //...
        set{ _Inner = value; }
    }

Какой смысл создавать сеттер для внутреннего контроля? Я не могу этого понять. Предполагается, что экземпляр внутреннего элемента управления создается из разметки во внешнем элементе управления, и именно этот созданный экземпляр должен работать с html-якорем. Если я добавлю эти строки в свойство Inner в Outer.ascx.cs

set
{
    this._Inner = (ASP.inner_ascx)value;
}

Я получу исключение нулевой ссылки, как в исходном случае. Я думаю, что установщик дает указание сборщику страниц ASP.Net создать еще один экземпляр внутреннего элемента управления и установить его с помощью свойства Inner. Я не уверен, но если у вас есть время, вы можете точно знать, как это происходит, изучив файлы cs, сгенерированные компоновщиком страниц, они находятся во временных файлах ASP.Net.

1 голос
/ 03 мая 2012

Если OuterControl.ascx выглядит как

<%@ Register TagPrefix="uc1" TagName="InnerControl" Src="InnerControl.ascx" %>
<uc1:InnerControl runat="server" ID="_Inner" />

, тогда проблема в том, что свойство OuterControl.Inner имеет метод доступа set. Удалите метод доступа set, чтобы решить проблему:

[PersistenceMode(PersistenceMode.InnerProperty)]
public InnerControl Inner
{
    get { return _Inner; }
}

Объяснение: Если OuterControl.Inner имеет set аксессор ...

  1. Во-первых, ASP.NET создает экземпляр ASP.outercontrol_ascx (динамически генерируемый класс, производный от OuterControl,), который, в свою очередь, создает экземпляр ASP.innercontrol_ascx (динамически генерируемый класс, производный от InnerControl).
  2. Далее, поскольку свойство Inner имеет метод доступа set, ASP.NET создает новый экземпляр InnerControl ( не ASP.innercontrol_ascx, как вы могли бы ожидать) и пытается инициализировать его Url свойство до "#". Конечно, это выдает NullReferenceException, потому что linkHref создается ASP.innercontrol_ascx, а не InnerControl.
  3. Наконец (если исключение не было выдано), ASP.NET устанавливает Inner для экземпляра InnerControl, созданного на шаге 2.

Если OuterControl.Inner не имеет аксессора set, то на шаге 2 ASP.NET просто устанавливает OuterCtrl1.Inner.Url на "#", а шаг 3 пропускается.

Примечание: Пара других ответов заявляют, что элементы управления созданы или инициализированы в Init. Это неверно; элементы управления, объявленные в разметке, создаются и инициализируются очень рано в жизненном цикле страницы, в FrameworkInitialize (что происходит до PreInit).

0 голосов
/ 16 мая 2009

Вы сказали:

// The private 'linkHref' control is declared
// in the .ascx markup of the control

Изменится ли что-нибудь, если вы сделаете этот элемент защищенным?

0 голосов
/ 17 мая 2009

Дерево управления строится после Init (); на самом деле Init () - это место для добавления ваших элементов управления в дерево до десериализации состояния представления. Вы не можете получить доступ к linkHref до того, как будет построено дерево управления.

Одним из возможных решений является сохранение URL-адреса, пока вы не сможете его использовать. Внутри внутреннего элемента управления измените свойство Url на простую строку:

public string Url { get; set; }

В обработчике событий Load или PreRender внутреннего элемента управления передайте строку в (теперь инициализированный) объект linkHref:

linkHref.HRef = value;
0 голосов
/ 09 января 2009

Вы устанавливаете HRef на метод OnInit страницы? Если это так, попробуйте переместить назначение в Page_Load.

Управляет Инициативой от самого внешнего до самого внутреннего. Это означает, что если вы присвоите значение в OnInit страницы, элементы управления еще не инициализированы.

Вот достойный документ на жизненном цикле страницы: http://www.codeproject.com/KB/aspnet/lifecycle.aspx

...