上一篇文章介绍了实现3种形式复杂属性的具体方法。为了加深读者对于这些实现方法的理解深度,本文详细讲解了一个利用ASP.NET 2.0技术创建复杂属性的示例。
1. 示例应用
本文所实现的示例很简单,其核心是通过实现自定义服务器控件的连字符形式复杂属性来说明复杂属性的实现方法,其中使用了上一篇文章中介绍的内容。示例效果图如图1所示。
图1
如图1所示,页面显示了公司所在城市、姓名、性别和职务信息。这些内容是定义服务器控件呈现的结果,其中公司所在城市由简单属性City设置,姓名、性别和职务由复杂属性Employee设置,其中包括子属性Name,Sex和Title设置。下面列举了示例应用程序的Default.aspx文件源代码。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="WebControlLibrary" Assembly="WebControlLibrary" TagPrefix="Cp" %>
<!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>
<Cp:Company ID="demo1" runat="server" City="重庆" Employee-Name="小李" Employee-Sex="男" Employee-Title="销售经理" />
</div>
</form>
</body>
</html>
如上代码所示,主要设置了@ Register指令和自定义服务器控件Company。前者用于为页面引入自定义服务器控件Company,从而实现控件在页面中的应用。在自定义服务器控件Company中主要设置了City、Employee-Name、Employee-Sex和Employee-Title。同时,在开发人员编码过程中将会发现,以上4个属性均为Visual Studio 2005的智能感知功能所支持。
另外,读者还可以以另一种非连字符形式设置Company控件属性。具体代码如下所示:
<Cp:Company ID="Company1" runat="server" City="重庆">
<Employee Name="小李" Sex="男" Title="销售经理">
</Employee>
</Cp:Company>
实际上,以上设置Company控件属性的方法与前文利用连字符设置属性的方法是完全一致的。对于所有连字符形式属性,可以任意使用二者之一。如果是基于代码可读性的角度而言,后者比前者的可读性更强一些。
2. 实现方法
上一小节中的Default.aspx页面所包含的Company控件具有3个连字符形式复杂属性。它们是如何实现的呢?实际上,实现这种形式的复杂属性关键是,在自定义服务器控件实现过程中,对复杂属性及其子属性设置特定的设计时元数据。
对于复杂属性而言,主要在该属性实现前设置两个设计时元数据:DesignerSerializationVisibility和NotifyParentProperty。DesignerSerializationVisibility用于指定在设计时序列化组件上的属性时,所使用的持久性类型。NotifyParentProperty可使得属性浏览器中对子属性的修改通知一直上传到对象模型,并在被修改了子属性的控件中产生修改通知。对于子属性的设计时元数据设置比较简单,只需在子属性实现前设置一个NotifyParentProperty即可。
实现自定义服务器控件Company涉及两个文件:Company.cs和Employee.cs。前者是自定义服务器控件的实现主体,其中包括各种属性设置、控件呈现方法RenderContents等等。后者用于实现复杂属性Employee。下面首先列举了Company.cs文件源代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary{
[DefaultProperty("Text")]
[ToolboxData("<{0}:Company runat=server></{0}:Company>")]
public class Company : WebControl {
private Employee employee; //实现属性City
[ Bindable(true), Category("Appearance"), DefaultValue(""), Description("公司所在城市") ]
public string City {
get {
string _city = (String)ViewState["City"];
return ((_city == null)?String.Empty:_city);
}
set { ViewState["City"] = value; }
} //实现属性Employee
[ Bindable(true), Category("Appearance"), Description("员工信息"), DesignerSerializationVisibility( DesignerSerializationVisibility.Content), NotifyParentProperty(true) ]
public Employee Employee {
get {
if (employee == null) {
employee = new Employee();
}
return employee;
}
} // 重写RenderContents方法,自定义实现控件呈现
protected override void RenderContents(HtmlTextWriter output) {
output.Write("公司所在城市:");
output.Write(City);
output.WriteBreak();
output.Write("姓名:");
output.Write(Employee.Name.ToString());
output.WriteBreak();
output.Write("性别:");
output.Write(Employee.Sex.ToString());
output.WriteBreak();
output.Write("职务:");
output.Write(Employee.Title.ToString());
}
}
}
以上代码显示了自定义服务器控件Company的实现,其中主要包括了一些属性和RenderContents方法的内容。具体属性包括2个:一个是简单属性City,另一个是复杂属性Employee。简单属性City的实现使用了视图状态ViewState。复杂属性Employee则有些特别,其类型是一个类Employee。同时,该属性还设置了两个元数据属性:DesignerSerializationVisibility(DesignerSerializationVisibility.Content)和NotifyParentProperty(true)。前者可用于指定序列化程序应该序列化属性的内容即子属性,后者则可使得属性浏览器中对子属性的修改通知一直上传到对象模型,并在被修改了子属性的控件中产生修改通知。以上两个设计时元数据属性的设置是实现连字符形式复杂属性的关键之一。另一个关键之处在于为在实现复杂属性的子属性时未其设置元数据属性。
下面列举了具体实现复杂属性Employee的Employee.cs文件源代码。
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;
namespace WebControlLibrary{
public class Employee {
private string _name;
private string _sex;
private string _title; //实现构造函数1
public Employee() { } //实现构造函数2
public Employee(String Name, String Sex, String Title) {
_name = Name; _sex = Sex; _title = Title;
} //实现属性Name
[ Bindable(true), Category("Appearance"), DefaultValue(""), Description("员工姓名"), NotifyParentProperty(true) ]
public String Name {
get { return _name; }
set { _name = value; }
} //实现属性Sex
[ Bindable(true), Category("Appearance"), DefaultValue(""), Description("员工性别"), NotifyParentProperty(true) ]
public String Sex {
get { return _sex; }
set { _sex = value; }
} //实现属性Title
[ Bindable(true), Category("Appearance"), DefaultValue(""), Description("员工职务"), NotifyParentProperty(true) ]
public String Title {
get { return _title; }
set { _title = value; }
}
}
}
以上代码实现了Employee类,其中包括构造函数和属性Name、Sex和Title。读者需要注意,为了实现连字符形式复杂属性,以上3个属性都必须设置元数据属性NotifyParentProperty(true)。这样,当子属性发生修改时,.NET框架将自动产生修改通知,并且通知到父属性Employee。
3. 小结
本文通过一个典型示例介绍了创建连字符形式复杂属性的实现方法。对于其他形式的复杂属性,例如,内部嵌套形式复杂属性、内部嵌套形式默认复杂属性等,本文将不再做更多解释。实际上,实现复杂属性是有其自身规律可循的。只要读者能够按照规定的方法实现,基本上都不会出现太大的问题。