zoukankan      html  css  js  c++  java
  • 《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实现可以换皮肤的控件一文的例子,也许有助您理解:)
  • 相关阅读:
    “XXXXX” is damaged and can’t be opened. You should move it to the Trash 解决方案
    深入浅出 eBPF 安全项目 Tracee
    Unity3d开发的知名大型游戏案例
    Unity 3D 拥有强大的编辑界面
    Unity 3D物理引擎详解
    Unity 3D图形用户界面及常用控件
    Unity 3D的视图与相应的基础操作方法
    Unity Technologies 公司开发的三维游戏制作引擎——Unity 3D
    重学计算机
    windows cmd用户操作,添加,设备管理员组,允许修改密码
  • 原文地址:https://www.cnblogs.com/zhuor/p/308910.html
Copyright © 2011-2022 走看看