这篇文档包含一个有关CSK的结构简介。文档的第一部分主要介绍CSK的两个特点:皮肤和由数据库生成页面和目录,你可以学到这两个特点是怎么实现的。
这篇文档的第二部分讨论修改CSK的方法。第一,你将学习怎么建立一个新的主题,以之用来改变站点的外观。接下来学习的是怎么建立一个在CSK平台上工作的模型组件,例如像Jobs模型组件。
Part 1:CSK的特点
★ 皮肤系统
CSK是建立在asp.net社区应用程序上的(www.asp.net)。就像ASP.NET社区应用程序一样,它也是利用皮肤系统从代码上决定用户接口。
一共有四种类型的皮肤供CSK使用:
Page Skins - Page Skin 决定所有层上的页面。例如,它可以决定栏目导航在页面上的位置。一般地,一个站点只选一种Page Skin。
Content Skins - Content Skin被用来组织页面上的内容部分。例如,一个Login页面的外观就是由Content Skin决定的。一般地,Content Skin 和站点的页面是一对一的关系。
Control Skins - Control Skin是用来定制一个控制的外观。例如,栏目导航控制的外观就是由Control Skin实现的。(还有好多种控制,如用户等)
Template Skins - Template Skin决定了控制模板的外观,像在一个Repeater控制里的ItemTemplate and HeaderTemplate。与comment views的区别,例如threaded,embedded,nested,通过不同的模板皮肤来实现。
无论何时,当你打开一个CSK页面时,一组皮肤被调用以之决定页面外观。例如,你一旦为了站点选择了robotico主题,并请求了一个login页面,下面的皮肤被调用了(不是完整的列表):
Robotico Page Skin 位于:/Themes/Robotico/Skins/PageSkins/Default.ascx
Login Page Content Skin 位于:/Themes/Robotico/Skins/ContentSkins/User_login.ascx
Section Menu Control Skin 位于:
/Themes/Robotico/Skins/ControlSkins/Sections_SectionMenu.ascx
余下的部分是怎样完全生成的呢?事实上,皮肤支持继承。如果一个皮肤不能在特定的主题中被调用的话,系统就自动从缺省主题中调用。这是非常重要的一点,它意味着当你建立一个新的主题时,不需要建立每一个皮肤和控制。你只需要覆盖你想修改遥皮肤即可。
皮肤是由HTML代码以及asp.net控制(不是代码)组成的。例如,Login页面的皮肤User_login.ascx可能是如下的一些内容:
<%@ Control %>
<p>
Login
</p>
Please enter your username and password below.
<br>
If you are a new user, click <a href="Users_Register.aspx">here</a> to register.
<asp:Panel id="pnlInvalidUsername" Runat="Server">
The username you entered is invalid!
</asp:Panel>
<asp:Panel id="pnlInvalidPassword" Runat="Server">
The password you entered is invalid!
</asp:Panel>
<table>
<tr>
<td>Username:</td>
<td><asp:TextBox id="txtUsername" Columns="20" runat="server" /></td>
</tr>
<tr>
<td>Password:</td>
<td><asp:TextBox id="txtPassword" Columns="20" TextMode="Password" runat="server" /></td>
</tr>
<tr>
<td colspan="2"><asp:Button id="btnLogin" runat="server" Text="Login" /></td>
</tr>
</table>
<p>
Click <asp:HyperLink id="lnkPasswordReminder" Runat="Server" Text="here" /> for a password reminder.
</p>
CSK所有页面都是用类来实现的,这些类都是从SkinnedCommunityControl这个基础类派生的。这个基础类负责具体的皮肤控制。
SkinnedCommunityControl类有一个重要的属性和一个重要的方法需要重写:SkinType属性和InitializeSkin()方法。SkinType属性指定了皮肤间的联合类型,这些皮肤有(ContentSkin,ControlSkin,TemplateSkin)。InitializeSkin()方法被用来重组所有的控制,以达到在代码中指定的控制属性都可以被执行。
例如,login页面的类文件(login.vb 或者login.cs)可能如下所示:
Login.cs
namespace ASPNET.StarterKit.Communities {
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using ASPNET.StarterKit.Communities;
using System.Web.Security;
//*********************************************************************
//
// Login Class
//
// Represents the user login page which enables
// users to login to the community.
//
//*********************************************************************
public class Login : SkinnedCommunityControl {
string _skinFileName = "Users_Login.ascx";
TextBox txtUsername;
TextBox txtPassword;
CheckBox chkPersist;
Button btnLogin;
Panel pnlInvalidUsername;
Panel pnlInvalidPassword;
HyperLink lnkPasswordReminder;
//*********************************************************************
//
// Login Constructor
//
// Calls the base SkinnedCommunityControl constructor
// and assigns the default page skin.
//
//*********************************************************************
public Login() : base() {
// Assign a default skin file name
if (SkinFileName == null)
SkinFileName = _skinFileName;
}
//*********************************************************************
//
// SkinType Property
//
// Specifies the skins directory where this page's skin file is located.
//
//*********************************************************************
override protected string SkinType {
get { return "ContentSkins"; }
}
//*********************************************************************
//
// InitializeSkin Method
//
// Retrieves all the controls from the Page Skin
//
//*********************************************************************
override protected void InitializeSkin(Control skin) {
// Find the Username TextBox
txtUsername = (TextBox)GetControl(skin, "txtUsername");
// Find the Password TextBox
txtPassword = (TextBox)GetControl(skin, "txtPassword");
txtPassword.TextChanged += new System.EventHandler(btnLogin_Click);
// Find the Persist Checkbox (Optional)
chkPersist = (CheckBox)GetOptionalControl(skin, "chkPersist");
// Find the Invalid Username Panel
pnlInvalidUsername = (Panel)GetControl(skin, "pnlInvalidUsername");
pnlInvalidUsername.Visible = false;
// Find the Invalid Password Panel
pnlInvalidPassword = (Panel)GetControl(skin, "pnlInvalidPassword");
pnlInvalidPassword.Visible = false;
// Find Login Button
btnLogin = (Button)GetControl(skin, "btnLogin");
btnLogin.Click += new System.EventHandler(btnLogin_Click);
// Find password reminder
lnkPasswordReminder = (HyperLink)GetControl(skin, "lnkPasswordReminder");
lnkPasswordReminder.NavigateUrl = "~/Users_PasswordReminder.aspx";
}
//*********************************************************************
//
// btnLogin_Click Method
//
// Checks username and password against database. If
// everything checks, logins the user.
//
//*********************************************************************
void btnLogin_Click(Object s, EventArgs e) {
bool blPersist = false;
// Determine whether password should be persisted
if (chkPersist != null)
blPersist = chkPersist.Checked;
// Either login or display error message
switch ( UserUtility.LoginUser(txtUsername.Text,txtPassword.Text) ) {
case 0: // Success!
FormsAuthentication.SetAuthCookie(txtUsername.Text, blPersist);
string redirectUrl = FormsAuthentication.GetRedirectUrl(txtUsername.Text, blPersist).ToLower();
if (redirectUrl.IndexOf("users_logout.aspx") == -1)
Context.Response.Redirect(redirectUrl);
else
Context.Response.Redirect(CommunityGlobals.ResolveBase("default.aspx"));
break;
case 1: // Invalid Password
pnlInvalidPassword.Visible = true;
pnlInvalidUsername.Visible = false;
break;
case 2: // Invalid Username
pnlInvalidUsername.Visible = true;
pnlInvalidPassword.Visible = false;
break;
}
}
}
}
Login类的SkinType属性指定了与类联合的皮肤为ContentSkin,也就是说控制系统将试图从选定的主题目录的ContentSkins中取得皮肤。
InitializeSkin()方法使用GetControl()取得Content Skin所有控件。GetControl()是SkinnedCommunityControl类的一个方法,它可以从一个皮肤中取得控件的信息。在这里,InitializeSkin()从content skin中抽取了txtUsername和txtPassword TextBoxes 和btnLogin Button等控件的信息。
从Content Skin中取得了btnLogin Button控件信息后,这个控件就和btnLogin_Click事件处理句柄建立联系了。当客户点击login按钮时,btnLogin_Click()就执行判定用户是否被允许操作数据库。
CSK中所有页面都像Login一样实现相同的操作。每个页都包含一个类和一个皮肤。皮肤决定了页面的外观,类中包含了所有的页面功能部分。
CSK中所有的页面都实现InitializeSkin()方法,使之可以调用与这个页面联合在一起的皮肤中的控件。当控件从皮肤中被取出后,能够在代码中修改控件的属性。
这种建立页面方法的优点在于能够快速(原词为dramatically,戏剧性地,急剧地,在这里找不到该如何表达的中文)地、很简单地通过与页面联合不同的皮肤而改变其外观。当你选择了一个不同的主题后,像ARC/Robotico/Professional等,不同的皮肤会与页面联合起来并使站点的外观得到迅速改变。
★ 从数据库产生页面和目录
在整个CSK中,真正的.aspx文件只有一个,它就是位于根目录下的CommunityDefault.aspx文件。它负责显示其它任何被请求的页面,不用理会这个页面是从站点的哪个页面请求的。
CSK中所有的页面和目录都是从数据库的表中动态生成的,无论何时,当你请求一个页面后,一个名叫Communities 的HTTP模型组件马上处理这个请求。模型组件会招待以下几个动作:
1、确定与页面联合在一起的站点信息;
2、确定与页面联合在一起的栏目信息;
3、确定与页面联合在一起的页面信息;
4、确定与页面联合在一起的用户信息;
5、存储以上所有信息(站点、栏目、页面等);
6、使用Context.RewritePath()方法,把请求重定向到CommunityDefault.aspx页面。
CommunityDefault.aspx使用由Communities模型组件收集的信息来生成待显示的目标页面。
CommunityInfo/SectionInfo/PageInfo/UserInfo四个对象的介绍
Communities 模型组件把Community information,Section information,Page information和User information存储在了HttpContext 对象的Items collection中。也就是说信息能够在类和控件的页面请求生存期内被取回(老外写得很咬口,也就是能够被编程控制)。
Community information被描述成一个名为CommunityInfo的类,这个类的名字用站点的名字来描述;
Section information 被描述成一个名为SectionInfo的类,这个类有很多属性,像Title、Path、Description等,用它们来描述一个指定栏目的信息;
Page information 被描述成一个名为PageInfo的类,这个类包括了一些通过页面显示的诸如内容的类型的值的属性(例如这个页面是一个文章系统还是一个图片系统);
最后,User information被描述成一个名为UserInfo的对象,这个对象描述了和用户绑定在一起的用户名、用户角色以及权限等信息。例如,UserInfo对象有一个名为MayAdd的属性,是用来确定用户是否有权在一个section中增加内容。
你能使用类似以下代码来在类和控件中使用以上这些信息:
C#
CommunityInfo objCommunityInfo =
(CommunityInfo)System.Web.HttpContext.Current.Items["CommunityInfo"];
SectionInfo objSectionInfo =
(SectionInfo)System.Web.HttpContext.Current.Items["SectionInfo"];
PageInfo objPageInfo =
(PageInfo)System.Web.HttpContext.Current.Items["PageInfo"];
UserInfo objUserInfo =
(UserInfo)System.Web.HttpContext.Current.Items["UserInfo"];
CSK页面的类型
CSK使用以下三种页面类型:
Named Pages –定义了一个静态的内容,像login.aspx以及Register.aspx等页面,生成这些页面的信息存储在内容中;
Section Default Pages –当你打开一个section的缺省页面时就会打开section default pages了。例如:文章发布栏目和相片浏览栏目的缺省页面被打开时。生成Section Default Page所需的信息存放在内存中;
Content Pages –描述了一个数据库中独一无二的内容项目。例如,一个内容页面可能会描述一个特殊的文章、下载等文件,它不是存在内存中,你每次页面被请求时,必须从数据库中取出。
Community, page and section information 分别存在几个数据库的表中:
· Community_Communities – 所有站点的列表
· Community_Sections – 所有站点栏目的列表
· Community_NamedPages – 所有站点的Named Page列表
· Community_ContentPages – 所有站点的Content Page列表
· Community_PageTypes – 所有站点页面的类型列表
当CSK程序启动后,它会把Community_Communies/Community_Sections/and Community_NamedPages数据表调入内存,当有页面请求发生时,Communities模型组件会在内存中执行一个查询动作,并把被请求的section和Page的信息取回(供控制程序用)。
如果你请求的是一个Content Page,例如一个特定的文章或图片,Communities模型组件将执行数据库查询操作来取得需要的信息。
Part 2:修改CSK
CSK所有的源代码已经包含在你下载的套件中了,你能修改包含在CSK中的主题和代码来完成任何你想达到的目的。
创建一个新的主题
使用CSK来创建一个新的主题非常简单,只要找一个现存的主题,比如Arc主题,把它复制到一个新的目录,并修改皮肤就行了。你一旦在主题目录下增加了一个新的目录后,这个主题就会在后台管理页面中自动显示。
所有的主题都位于Communities目录下,它有如下的目录结构:
/Communities
/Common
/Themes
/Community1
/Themes
/Community2
/Themes
Communities/Common目录包含了一些能够被其它站点使用的主题,单独的站点能拥有只有它们自己能使用的主题目录。
主题目录结构如下:
/Themes
/Default
/Skins
/ContentSkins
/ControlSkins
/PageSkins
/TemplateSkins
/Styles
Default.aspx位于Skin的PageSkins子目录下,它确定了这个主题下所有页面布局的皮肤。你能修改皮肤来改变这个页面的行数、section和topic菜单的位置以及其它一些有关一般性的页面布局。
如果你想改变指定页面的外观,你需要通过在新主题的ContentSkins增加新的皮肤文件来覆盖缺省页面皮肤。例如,你现在想修改Login.aspx页面,就可以把位于缺省主题ContentSkins目录下的User_Login.aspx文件复制到新的主题中,然后再进行修改。
你不需要为像缺省主题那样为每个皮肤建立一个新的皮肤或控件,假如你没有提供皮肤,系统将自己从缺省主题中找到对应的替代皮肤。
主题目录下另一个子目录是Styles,它包含了一个或多个能够在主题中应用的样式表。所有加到Styles目录中的样式表(必须以.css作为扩展名)能够自动显示在后台管理页面中的样式表下拉选单中。
建立一个新的模型组件
你能建立一个新的CSK模型组件,例如,你可能需要建立一个新的用于显示工作列表的模型组件(Job Module),而建立一个这样的模型组件,你需要下面的基本步骤:
1、建立从基础类SkinnedCommunityControl派生类,这个类描述模型组件中的页面部分;
2、建立用户控件,以描述这个模型组件中页面所使用的Content Skins;
3、注册一个新的页面在数据库的Community_NamedPages 和Community_PageTypes中。
例如,上面的那个例子,你就需要完成包括以下几个步骤:
1、Jobs Section Page – 显示一个Jobs的列表;
2、Job Page – 显示一个特定的Job;
3、Add Job Page – 允许用户新增一个Job;
4、Edit Job Page – 允许用户修改一个现有的Job;
建立这些页面,需要从建立四个类文件着手。CSK框架包含了这些页面的每一个正确的基础类:
· ContentListPage
· ContentItemPage
· ContentAddPage
· ContentEditPage
这四个基础类位于/Engine/Framework/BaseClasses 目录。
作为一个实现这些类的示例,请参考包含在CSK中的模型组件的源代码。例如,Books模型组件包含了BookSection,Book,AddBook和EditBook类。
接下来,就是要给这些类建立皮肤文件了。你需要在主题目录的ContentSkins目录中增加以下几个皮肤文件:
· Jobs_JobSection.ascx
· Jobs_Job.ascx
· Jobs_AddJob.ascx
注意,只列出了三个皮肤文件,在CSK所有的标准模型组件都使用相同的增加和编辑页面。
一个content skins的例子,可以参考Books模型组件使用的Content skins。Book模型组件包含了以下几个皮肤(能在/Communities/Common/Themes/Default/Skins/ContentSkins 目录中找到):
· Books_BookSection.ascx
· Books_Book.ascx
· Books_AddBook.ascx
建立完页面后,在他们显示之前,你还需要在数据库中注册它们。需要在Community_NamedPages和Community_PageTypes两个数据表中增加记录。
要了解Book section是怎样用SQL语句注册的,你可能参考有关说明书,作为一个例子,下面是存储的过程来注册BookSection页面的:
IF NOT EXISTS (SELECT * FROM Community_PageTypes WHERE pageType_Name='Book Section')
BEGIN
INSERT INTO Community_PageTypes
(
pageType_Name,
pageType_Description,
pageType_PageContent,
pageType_isSectionType
)
VALUES
(
'Book Section',
'Contains book listings',
'ASPNET.StarterKit.Communities.Books.BookSection',
1
)
END
ELSE
PRINT 'WARNING: The Books Module has already been registered.'
在这个语句被执行后, Books模型组件就会在你的管理页面中通过增加栏目或编辑栏目来增加或编辑这个模型组件了。
时间仓促,没有仔细校正!
2004/8/20