• 《Asp.Net Forums2.0深入分析》之 Asp.Net Forums是如何实现代码分离和换皮肤


    在Visual Studio中开发Web项目,Web 窗体页由两部分组成:视觉元素(HTML服务器控件和静态文本)和该页的编程逻辑。 一般将这两个组成部分分别存储在一个单独的文件中。可视元素在一个 .ASPx 文件中创建,而代码位于一个单独的类文件中(.ASPx.vb 或 .ASPx.cs)。或者有时候也会在同一文件中创建视觉元素和代码。

    而在ASP.Net Forums的Web窗体页中没有找到我们熟悉的.ASPx.cs文件,也没有发现任何C#代码,取而代之是一个个控件,代码在哪里?!

    下面将以login.ASPx为例详细说明ASP.Net Forums是如何实现代码分离和换皮肤的:
    首先我们看看login.ASPx在两种皮肤样式下的运行效果
    (Theme:default)(Theme:ElectricMidnight)

    只是更改了一下ASP.Net Forums的默认皮肤,同样是Login.ASPx,显示的是两种不同的皮肤样式。先回想一下VS.Net中,先不论换皮肤功能,如果我们要实现一个登陆页面,那么我们在ASPx或Ascx页中将输入帐号密码的TextBox、登陆的Button拖入,在编辑区双击Button,写上对Button点击事件处理的代码,多么方便,大部分代码都由VS.Net为我们完成了。

    我们再来看Login.ASPx的源码:


    <%@ Import Namespace="ASPNetForums.Components" %>
    <%@ Register TagPrefix="Forums" Namespace="ASPNetForums.Controls" Assembly="ASPNetForums.Controls" %>
    <%@ Register TagPrefix="mp" Namespace="MetaBuilders.WebControls.MasterPages" Assembly="MetaBuilders.WebControls.MasterPages" %>

    <mp:ContentContainer runat="server" id="MPContainer" MasterPageFile="~/Themes/MasterPage.ascx">
    <mp:Content id="MainContent" runat="server">
    <p align="center">
    <Forums:NavigationMenu DisplayTitle="true" id="Navigationmenu1" runat="server" />
    <br />
    <br />
    <br />
    <Forums:Login runat="server" id="PostView1" />
    </p>
    </mp:Content>
    </mp:ContentContainer>
    注:其中 <mp:***> ,这个是一个第三方控件,其目的是为了保证界面的一致性,提取页面间的重复代码。

    从源码中我们没有看到任何构成Login.ASPx页面效果的TextBox、Button等基本元素。甚至没有发现一行C#代码,不过如果您对页面控件比较熟悉不难发现原来ASP.Net Forums中将登陆的界面封装为了控件(在此对页面控件并不作专门介绍,如果您对控件相关知识还比较陌生的话,强烈推荐您查阅相关资料或书籍)。 原来登陆界面的实现就是在<Forums:Login runat="server" id="PostView1" />控件中,从
    <%@ Register TagPrefix="Forums" Namespace="ASPNetForums.Controls" Assembly="ASPNetForums.Controls" %>
    我们可以知道Login控件对应的类应该为:ASPNetForums.Controls.Login,在VS.Net中,切换到类视图,找到ASPNetForums.Controls.Login并转到对应文件:

    (该图告诉您如何快速的查找控件对应的文件)
     
    从代码中看到的该控件是从SkinnedForumWebControl类继承的:

    public class Login : SkinnedForumWebControl {  // 从 SkinnedForumWebControl 基类继承
    ......
    }

    我们还是先看看基类SkinnedForumWebControl。
    using System;
    using System.Drawing;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using ASPNetForums;
    using ASPNetForums.Components;
    using System.ComponentModel;
    using System.IO;
    using System.Web.Security;
    using ASPNetForums.Enumerations;

    namespace ASPNetForums.Controls {

    [
    ParseChildren(true)
    ]
    /// <summary>
    /// 几乎ASP.Net Forums中所有控件的基类,继承自WebControl,并实现INamingContainer接口
    /// </summary>
    public abstract class SkinnedForumWebControl : WebControl, INamingContainer {

    ForumContext forumContext = ForumContext.Current;
    string skinFilename = null;
    string skinName = null;
    string returnURL = null;
    ForumMode mode = ForumMode.User;


    public SkinnedForumWebControl() {

    // 使用的皮肤——如果是匿名用户,则使用系统默认样式
    //
    if (forumContext.User.IsAnonymous) {
    skinName = Globals.Skin;
    }
    else {
    skinName = forumContext.User.Theme;
    }

    }



    /// <summary>
    /// 当开发复合服务器控件或模板服务器控件时,必须重写此方法。
    /// 通知使用基于合成的实现的服务器控件创建它们包含的任何子控件,以便为回发或呈现做准备。
    /// </summary>
    protected override void CreateChildControls() {
    Control skin;

    // 装载用户控件
    skin = LoadSkin();

    // 初始化控件
    InitializeSkin(skin);

    Controls.Add(skin);
    }


    /// <summary>
    /// 通过SkinName和SkinFilename找出用户控件文件的路径,装载该用户控件后的Control对象
    /// </summary>
    /// <returns></returns>
    protected Control LoadSkin() {
    Control skin;
    // 用户控件文件所在位置
    string skinPath = Globals.GetSkinPath() + "/Skins/" + SkinFilename.TrimStart('/');
    string defaultSkinPath = Globals.ApplicationPath + "/Themes/default/Skins/" + SkinFilename.TrimStart('/');

    // 必须要有SkinFilename属性
    if (SkinFilename == null)
    throw new Exception("You must specify a skin.");

    // 从用户控件文件获取 UserControl 对象。
    try {
    skin = Page.LoadControl(skinPath);
    }
    catch (FileNotFoundException) {

    // 如果没有找到指定皮肤的用户控件文件,装载默认皮肤下的控件文件
    try {
    skin = Page.LoadControl(defaultSkinPath);
    }
    catch (FileNotFoundException) {
    throw new Exception("Critical error: The skinfile " + skinPath + " could not be found. The skin must exist for this control to render.");
    }
    }
    return skin;
    }

    /// <summary>
    /// 初始化控件,并绑定控件数据
    /// </summary>
    /// <param name="skin"></param>
    protected abstract void InitializeSkin(Control skin);

    /// <summary>
    /// 用户控件文件(*.ascx)路径
    /// </summary>
    public string SkinFilename {
    get {
    return skinFilename;
    }
    set {
    skinFilename = value;
    }
    }


    /// <summary>
    /// 皮肤名
    /// </summary>
    protected string SkinName {
    get {
    return skinName;
    }
    set {
    skinName = value;
    }
    }

    public ForumMode Mode {
    get { return mode; }
    set { mode = value; }
    }

    }
    }
     
    从代码中可以看出,基类SkinnedForumWebControl继承自WebControl类并实现了INamingContainer接口。既然是自定义控件,自然就是从WebControl类继承。之所以实现INamingContainer接口,是因为在开发模板化控件时,应实现该接口以避免同一页上的命名冲突。
    在SkinnedForumWebControl中有两个重要的属性:SkinName 和 SkinFilename,分别表示皮肤名和用户控件文件路径。在ASP.Net Forums2.0中,在Web目录下有一个Themes目录,每种皮肤对应一个目录,例如default、ElectricMidnight,每个皮肤文件夹下有三个文件夹:image、Skins和style,分别用来存放该皮肤下对应的图片文件、用户控件文件(*.ascx)和样式表CSS文件。通过这两个属性,我们可以知道用户控件文件(*.ascx)的真实路径,例如我们的SkinName是default,SkinFilename是skin-login.ascx,那么用户控件的路径是Themes/default/skins/skin-login.ascx,同理,如果我们将皮肤样式换成ElectricMidnight,那么用户控件的路径将是 Themes/ElectricMidnight/skins/skin-login.ascx。

    还有两个重要的方法,一个是LoadSkin(),在该方法中,首先根据上面介绍的两个属性找出用户控件文件的路径,然后通过Page.LoadControl(defaultSkinPath)方法,从用户控件文件中获取 UserControl 对象。这也就是为什么皮肤不同,页面样式就不同,因为随着皮肤的不同,我们Load的用户控件文件也不同,我们只要将用户控件文件样式设置的不一样,就可以随着皮肤的不同显示的样式也不一样。
     


    但是光这些还不够,我们还需要识别ascx页中的页面控件。例如在skin-login.ascx中,我们必须知道哪个输入框是帐号的,那个输入框是密码的,知道用户是否点击了登陆按钮。回想VS.Net中,IDE自动帮我们把这些控件根据ID识别出来,在IDE中双击按钮,就可以直接加上响应点击事件的代码,多么方便。但是现在我们该如何?……

    基类中还有一个抽象的InitializeSkin(Control skin)方法,所有继承自SkinnedForumWebControl的控件都必须override该方法,因为我们可以在这个方法中来初始化控件,在LoadSkin()方法中我们已经通过Page.LoadControl(defaultSkinPath)方法返回了一个UserControl,现在我们在InitializeSkin(Control skin)中通过Control.FindControl 方法,在UserControl中搜索指定的服务器控件,并对控件进行绑定数据和事件。还是以Login控件为例,摘取ASPNetForums.Controls.Login类中的部分代码如下:

    public class Login : SkinnedForumWebControl {  // 从 SkinnedForumWebControl 基类继承
    string skinFilename = "Skin-Login.ascx"; // 默认皮肤文件
    TextBox username; // 帐号输入框
    TextBox password; // 密码输入框
    Button loginButton; // 登陆按钮
    }
    public Login() : base() {
    if (SkinFilename == null)
    SkinFilename = skinFilename; // 定义默认的皮肤文件
    }
    // 重写 InitializeSkin 初始化
    override protected void InitializeSkin(Control skin) {

    // 查找ascx页中ID是username的textbox控件
    username = (TextBox) skin.FindControl("username");

    // 查找ascx页中ID是password的textbox控件
    password = (TextBox) skin.FindControl("password");

    // 找到登陆按钮
    loginButton = (Button) skin.FindControl("loginButton");
    loginButton.Click += new System.EventHandler(LoginButton_Click); // 绑定登陆按钮的Click事件
    loginButton.Text = ResourceManager.GetString("LoginSmall_Button");

    }

    在skin-login.ascx中,我们的每个控件都有一个ID,例如用户名输入框的ID是username,这样,我们就可以在重写InitializeSkin(Control skin)的时候,利用username = (TextBox) skin.FindControl("username");这样的方法来一个个找到对应用户控件文件中的控件。并可以对绑定数据和事件,例如:loginButton.Click += new System.EventHandler(LoginButton_Click),绑定登陆按钮的Click事件。
    综上所述,ASP.Net Forums就是通过自定义控件来实现代码分离的,并通过在控件中动态装载用户控件文件(*.ASPx)来实现换皮肤功能的。当您在看ASP.Net Forums2.0源代码的时候,再也不要被<Forums:NavigationMenu DisplayTitle="true" id="Navigationmenu1" runat="server" />这样的代码所吓倒,直接切换到类视图,找到对应的类:ASPNetForums.Controls.NavigationMenu,从类代码中……

    如果您还不是很理解,您可以自己研读一下ASP.Net Forums2.0的源码,如果您觉得太复杂,请看模拟ASP.Net Forums实现可以换皮肤的控件一文的例子,也许有助您理解:)
  • 相关阅读:
    PHP 命名空间
    使用 htaccess 重写 url,隐藏查询字符串
    HTML 长文本换行
    Mac OS X 上的Apache配置
    无法debug断点跟踪JDK源代码——missing line number attributes的解决方法
    根据多条件删除还能这样写
    wm_concat()函数
    spring 事务-使用@Transactional 注解(事务隔离级别)
    spring 中常用的两种事务配置方式以及事务的传播性、隔离级别
    oracle 中SQL 语句开发语法 SELECT INTO含义
  • 原文地址:https://www.cnblogs.com/zhuor/p/308910.html
Copyright © 2020-2023  润新知