理解模板化用户控件的初学者教程





5.00/5 (13投票s)
从初学者的角度理解模板化 Web 用户控件。
引言
本文旨在从初学者的角度理解模板化 Web 用户控件。我们将尝试理解为什么可能需要模板化用户控件,以及模板化用户控件的好处是什么。
背景
当我们需要在多个地方使用具有一些共同功能的控件组时,我们就会求助于用户控件。用户控件结合了一组控件和功能,以便在站点的多个地方重用。
现在,让我们考虑一种场景,我们需要在 UI 上表示一些数据。我们需要在多个页面上表示这些数据,并且这些数据的布局/外观在不同位置可能不同。在这种情况下,我们能使用用户控件吗?这个问题的答案是 - 是的,我们可以。为了实现这种数据的重用,同时又能灵活地控制布局,我们需要创建一个模板化用户控件。
模板化用户控件将数据封装在内部,并为模板化用户控件的每个实例提供自定义数据布局的便利。有时,它也可能提供默认表示,并允许进行自定义。
使用代码
让我们考虑一个假设的场景,我们需要存储一个人的名字、姓氏和年龄。我们需要在网站的许多地方显示这些信息,但每个地方的布局可能不同。
实现模板化用户控件
我们需要做的第一件事是创建一个用户控件。一旦我们有了用户控件,我们就必须在该用户控件上放置一个占位符控件。顾名思义,这个占位符控件将作为提供默认表示的控件以及开发人员可能想要使用的自定义布局的占位符。
现在,在深入探讨如何实现此用户控件之前,让我们先完成所需的工作。我们将需要一个对象来表示数据。在我们的例子中,我们将创建一个 Person
类来存储人的名字、姓氏和年龄。让我们继续创建这个类。
public class PersonItem
{
private string firstName;
private string lastName;
private int age;
public PersonItem(string fName, string lName, int aGe)
{
this.firstName = fName;
this.lastName = lName;
this.age = aGe;
}
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
}
}
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}
现在我们有了现成的对象,接下来我们需要定义一个类,该类将用作用户可能在其自定义布局中添加的所有控件的容器。拥有此容器类的主要目的是在自定义布局就位的情况下提供 FindControl
功能。因此,我们可以将此类称为该控件的命名容器。这可以通过继承此类自 Control
类并实现 INamingContainer
接口来完成。此外,由于此容器内的控件将使用 Person
类的数据。此类还需要对 Person 类有一个引用。让我们现在创建这个类。
public class PersonItemContainer : Control, INamingContainer
{
private PersonItem person = null;
public PersonItemContainer(PersonItem item)
{
this.person = item;
}
public PersonItem Person
{
get
{
return person;
}
set
{
person = value;
}
}
}
现在看这个类,它显然包含对 Person
对象的引用,但这个类如何作为我们用户控件的命名容器还不清楚。为了使这个类作为我们对象的 NamingContainer,我们需要将这个类与我们刚刚创建的用户控件挂钩。为此,我们需要在用户控件的代码隐藏中创建一个类型为 ITemplate
的类成员和一个公共属性。
private ITemplate personTemplate;
public ITemplate PersonTemplate
{
get
{
return personTemplate;
}
set
{
personTemplate = value;
}
}
仍然不清楚它与 NamingContainer 类有什么关系。为了指定这一点,我们需要用 [TemplateContainer(typeof(PersonItemContainer))]
属性来修饰这个属性,即
private ITemplate personTemplate;
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(PersonItemContainer))]
public ITemplate PersonTemplate
{
get
{
return personTemplate;
}
set
{
personTemplate = value;
}
}
另一个属性 PersistenceMode
用于指定此用户控件将拥有此属性,并在 ASPX 标记中作为嵌套元素。完成此操作后,我们现在需要编写代码来提供控件的默认表示,并将控件的数据与用户提供的模板/布局挂钩。
protected void Page_Init(object sender, EventArgs e)
{
//lets first clear the placeholders to attach then to new data
this.phdPerson.Controls.Clear();
//but what if the user forgot to send the user information
if (Person == null)
{
phdPerson.Controls.Add(new LiteralControl("Please attach the control with a person object."));
}
else
{
//check if the templates are null we want to use simple literal
//The user wants the default representation of the control
if (PersonTemplate == null)
{
//Lets show the default representation of the control
phdPerson.Controls.Add(new LiteralControl("First Name: "));
phdPerson.Controls.Add(new LiteralControl(Person.FirstName));
phdPerson.Controls.Add(new LiteralControl("<br/>"));
phdPerson.Controls.Add(new LiteralControl("Last Name:"));
phdPerson.Controls.Add(new LiteralControl(Person.LastName));
phdPerson.Controls.Add(new LiteralControl("<br/>"));
phdPerson.Controls.Add(new LiteralControl("Age: "));
phdPerson.Controls.Add(new LiteralControl(Person.Age.ToString()));
}
else
{
PersonItemContainer container = new PersonItemContainer(this.Person);
this.PersonTemplate.InstantiateIn(container);
phdPerson.Controls.Add(container);
}
}
}
现在让我们看一下用户控件的完整代码隐藏,以理解完整的逻辑。
public partial class TemplateUCtrl : System.Web.UI.UserControl
{
//the template control specific stuff
private ITemplate personTemplate;
//the controls data specific stuff
private PersonItem personItem = null;
protected void Page_Init(object sender, EventArgs e)
{
//lets first clear the placeholders to attach then to new data
this.phdPerson.Controls.Clear();
//but what if the user forgot to send the user information
if (Person == null)
{
phdPerson.Controls.Add(new LiteralControl("Please attach the control with a person object."));
}
else
{
//check if the templates are null we want to use simple literal
//The user wants the default representation of the control
if (PersonTemplate == null)
{
//Lets show the default representation of the control
phdPerson.Controls.Add(new LiteralControl("First Name: "));
phdPerson.Controls.Add(new LiteralControl(Person.FirstName));
phdPerson.Controls.Add(new LiteralControl("<br/>"));
phdPerson.Controls.Add(new LiteralControl("Last Name: "));
phdPerson.Controls.Add(new LiteralControl(Person.LastName));
phdPerson.Controls.Add(new LiteralControl("<br/>"));
phdPerson.Controls.Add(new LiteralControl("Age: "));
phdPerson.Controls.Add(new LiteralControl(Person.Age.ToString()));
}
else
{
PersonItemContainer container = new PersonItemContainer(this.Person);
this.PersonTemplate.InstantiateIn(container);
phdPerson.Controls.Add(container);
}
}
}
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(PersonItemContainer))]
public ITemplate PersonTemplate
{
get
{
return personTemplate;
}
set
{
personTemplate = value;
}
}
public PersonItem Person
{
get
{
return personItem;
}
set
{
personItem = value;
}
}
}
使用模板化用户控件
让我们看看现在如何使用这个用户控件。让我们将此用户控件拖到一个页面上,并在代码隐藏中提供 Person
详细信息,如下所示:
protected void Page_PreInit(object sender, EventArgs e)
{
PersonItem item = new PersonItem("one", "two", 23);
TemplateUCtrl1.Person = item;
}
这将显示页面上用户控件的默认表示。
如果用户未能提供 Person 详细信息怎么办?为了检查这一点,让我们将另一个用户控件拖到页面上,并且不要为其分配 Person
对象。
因此,我们已经使用了默认的用户控件。现在,如果我们想自定义用户控件的布局,我们可以通过在用户控件中提供 PersonTemplate
来实现。让我们为第二个用户控件这样做(并也在代码隐藏中提供 person 对象。结果的标记和代码将如下所示:
<body>
<form id="form1" runat="server">
<div>
<uc1:TemplateUCtrl ID="TemplateUCtrl1" runat="server" />
<br />
<br />
<br />
<uc1:TemplateUCtrl ID="TemplateUCtrl2" runat="server">
<PersonTemplate>
Welcome Mr. <strong>'<%# Container.Person.FirstName %>'</strong>
<table>
<tr>
<td>
Person's first Name:
</td>
<td>
<asp:TextBox ID="TextBox1" runat="server"
Text="<%# Container.Person.FirstName %>"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Person's Last Name:
</td>
<td>
<asp:TextBox ID="TextBox2" runat="server"
Text="<%# Container.Person.LastName %>"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Person's Age:
</td>
<td>
<asp:TextBox ID="TextBox3" runat="server"
Text="<%# Container.Person.Age %>"></asp:TextBox>
</td>
</tr>
</table>
Give our best wishes to Mrs. <strong>'<%# Container.Person.LastName %>'</strong>
</PersonTemplate>
</uc1:TemplateUCtrl>
</div>
</form>
</body>
页面代码隐藏
public partial class _Default : System.Web.UI.Page
{
protected void Page_PreInit(object sender, EventArgs e)
{
PersonItem item = new PersonItem("one", "two", 23);
TemplateUCtrl1.Person = item;
TemplateUCtrl2.Person = item;
}
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
}
现在,当我们运行此应用程序时
在总结之前,让我们看看页面和控件的生命周期。
- 调用
Page
的Pre_Init
,它会分配用户控件的Person
对象。 - 调用我们添加的第一个用户控件的
Control
的Pre_Init
,它会渲染默认表示。 - 调用我们添加的第二个用户控件的
Control
的Pre_Init
,它会考虑到用户已经提供了一些自定义模板,并相应地渲染页面。 - 调用
Page
的Page_Load
,它将执行Page.Bind()
,这将告诉模板化用户控件将数据绑定到自定义布局。
关注点
模板化用户控件提供了一种非常简单的方法来拥有一个可以重用并具有一些常见功能的控件。它还允许用户为控件定义自己的布局。我们也许会在另一篇文章中讨论模板化服务器控件。
历史
- 2012年6月7日:修复了一些代码格式化问题。
- 2012年6月1日:第一个版本。