65.9K
CodeProject 正在变化。 阅读更多。
Home

真正动态的主页

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.10/5 (15投票s)

2007年2月12日

CPOL

4分钟阅读

viewsIcon

202440

downloadIcon

2284

以编程方式创建 ContentPlaceHolders 和 Contents

引言

母版页是 ASP.NET 2.0 中一个非常酷的功能。它们使您可以轻松地在整个网站中创建统一的外观和感觉。它们还提供了一种在运行时轻松设置/更改母版页的方法。这通过在 PreInit 事件中设置页面的 MasterPageFile 属性来实现。但是,这要求母版页具有 ContentPlaceHolders,并且页面上的 Content 控件通过 ContentPlaceHolderID 属性引用它们。本文重点介绍如何以编程方式创建 ContentPlaceHoldersContents 以及将它们链接在一起。

背景

当我第一次使用 ASP.NET 2.0 时,我使用的第一个功能之一就是母版页。我很快就学会了如何在运行时在它们之间切换。过了一段时间,我在做一个小型项目,目标是尽可能多地动态创建东西(类似于一个 Web 应用程序框架)。我曾想过使用存储在数据库(作为元数据)中的母版页,然后动态构建它们。我很快发现这项任务并不像看起来那么容易。它需要的不仅仅是如何通过代码创建控件的知识。我做了一些关于如何正确完成此任务的研究,并想分享这项技术,因此本文应运而生。

Using the Code

提供的示例包含两个页面和一个母版页。它的唯一目的是演示如何操作。在起始页(Default.aspx)上,您需要输入一个数字,按一个按钮,然后让魔法开始。尽管下一页上的视觉效果并不十分吸引人(只能看到一些文本),但魔法仍在后台发生。让我们看看发生了什么。为此,您应该对 ASP.NET 页面生命周期有一定的了解。

最重要的一步是创建页面上的内容。您会惊讶地发现,无需创建 Content 类。而是应该使用 Page 类的 AddContentTemplate 方法。您可能从未听说过此方法,这是因为它应用了 [EditorBrowsable(EditorBrowsableState.Never)] 属性,因此在 IntelliSense 中不可见。MSDN 提供了一些关于它的信息,您可以在这里阅读:此处。您需要两个参数,第一个是它将链接到的 ContentPlaceHolder 的名称(您只能在标记中设置 ContentPlaceHolderID 属性),第二个是 ITemplate。如果您从未处理过模板化控件,您可能会觉得这有点复杂,但实际上并非如此。但是,解释这一点仍然超出了本文的范围。在此示例中,所有内容都相同,但并非必须如此,而且更容易实现,并且足以用于演示目的。

protected void Page_PreInit(object sender, EventArgs e)
{
    //...same check as in the constructor of the Master Page 
    for (int i = 1; i < num + 1; i++)
        base.AddContentTemplate("ContentPlaceHolder" + i.ToString(),
            new CompiledTemplateBuilder(new BuildTemplateMethod(this.Build)));
}

在 ASP.NET 中使用构造函数是一个非常不常见的情况,但在这种情况下,没有其他地方可以放置所需的代码,因为母版页的 Init 事件已经太晚了,而且没有 PreInit 事件。您只需要将稍后要创建的 ContentPlaceHolder 控件的名称添加到 Master 类的 ContentPlaceHolders 集合中。请注意,您必须使用小写名称。

public MasterPage()
{
   object cphnum = HttpContext.Current.Session["cphnum"];
   if (cphnum == null || !(cphnum is int))
      return;
   num = (int)cphnum;
   for (int i = 1; i < num + 1; i++)
      base.ContentPlaceHolders.Add("contentplaceholder" + i.ToString()); 
}

最后,您应该实例化 ContentPlaceHolders 并将它们添加到页面的控件层次结构中。这也通过一个 for 循环来实现,以便创建所需数量的控件。为此,您必须使用母版页所基于的 ContentTemplates 集合,该集合的类型为 ITemplate

protected void Page_Init(object sender, EventArgs e)
{
    //...
    for (int i = 1; i < num + 1; i++)
    {
    ContentPlaceHolder cph = new ContentPlaceHolder();
    cph.ID = "ContentPlaceHolder" + i.ToString();
    PlaceHolder1.Controls.Add(cph);
    ((ITemplate)base.ContentTemplates["ContentPlaceHolder" +
        i.ToString()]).InstantiateIn(cph);
    }
}

现在您已经了解了基础知识,您可以轻松地扩展此示例,使其比当前形式更有用。

嵌套母版页 

使用此技术创建嵌套母版页也得到支持,但是需要对代码进行少量修改。理论上,嵌套母版页只不过是一个带有自己的 ContentPlaceHolders 的母版页,以及用于填充其父占位符的 Contents 的普通页面。请注意,在母版页可以使用内容填充其占位符之前,内容必须存在于内容页面上,但 init 事件的触发顺序相反,因此主母版页的事件先于嵌套母版页的事件触发,从而导致 NullReferenceException(或者在检查时,内容为空)。构造函数也太早了,不适合此目的,因为那样会破坏嵌套母版页和内容页面之间的协同作用,原因类似。因此,必须使用另一个称为 FrameworkInitialize() 的事件。它在所需的时​​间窗口触发,并且触发顺序正确,因此可以实现深度嵌套。此场景也有一个演示,请参阅上方下载列表中的内容。 

历史 

  • 2007年2月12日 - 文章首次发布
  • 2007年4月16日 - 添加了 VB.NET 演示
  • 2012年5月7日 - 添加了关于嵌套的部分 
© . All rights reserved.