杭州早教机构排名:创建ThemeSelector用户控件

来源:百度文库 编辑:中财网 时间:2024/05/03 10:12:07
现在已有了一个模板页面和两个主题,所以可以开发一个能够显示可用主题并且用户可进行选择的用户控件。完成这个控件后,可以把它放到模板页面中的themeselector DIV容器中。在创建用户控件之前,为了更好地组织文件,新建一个名为Controls的文件夹,该文件夹用来存放所有的用户控件,把它们同页面分开(选中项目,右击,在所出现的菜单中选择Add Folder | Regular folder,将其命名为Controls)。要创建用户控件,右击Controls 文件夹,从菜单中选择Add New Item | Web User Control,将新用户控件命名为ThemeSelector.ascx。该用户控件的内容非常简单,只包含一个字符串和下拉列表(DropDownList)。
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="ThemeSelector.ascx.cs" Inherits="ThemeSelector" %>
Theme:

注意下拉列表的AutoPostBack属性设置为true,这样用户在改变所选值后页面就会自动向服务器提交。填充主题下拉列表、加载所选定的主题是在控件的后台代码文件中实现的,您稍候将在基础页面类中看到这一点。在后台代码文件中,需要将一个helper方法返回的字符串数组填充到下拉列表中,然后将当前页面所用主题的名称作为下拉列表的选定项:
public partial class ThemeSelector : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
ddlThemes.DataSource = Helpers.GetThemes();
ddlThemes.DataBind();
ddlThemes.SelectedValue = this.Page.Theme;
}
}
GetThemes 方法是在 Helpers.cs 中定义的,此文件位于名为App_Code的特殊文件夹中。在运行时,ASP.NET 引擎会自动地对此文件夹中的文件进行编译,所以在项目运行之前不需要对它们进行编译。甚至可以在系统运行中修改C#源代码,然后进行刷新,当遇到新的请求时,系统就会把修改过的文件进行重编译,编译为新的临时程序集,然后加载。本书在后文中会对这种新的编译模型有更多的介绍,尤其是在第12章介绍项目部署时。
GetThemes 方法使用了 System.IO.Directory 类中的 GetDirectories 方法,它的功能是查找~/App_Themes 文件夹中所包含的所有文件夹,然后将这些文件夹的路径以数组的方式返回(此方法想要得到的是物理路径而不是URL,因此可以使用Server.MapPath方法通过URL来获得App_Themes文件夹的物理路径)。返回的字符串数组中包含的是完整路径,不只是文件夹名称,所以必须遍历数组,将每一项替换为该项所对应路径的文件夹部分(通过System.IO.Path.GetFileName静态方法)。数组在第一次填充后就保存在ASP.NET 缓存中,这样以后的请求会从缓存中读取数据,速度更快。下面是Helpers 类(App_Code\Helpers.cs)中的完整内容:
namespace MB.TheBeerHouse.UI
{
public static class Helpers
{
///
/// Returns an array with the names of all local Themes
///

public static string[] GetThemes()
{
if (HttpContext.Current.Cache["SiteThemes"] != null)
{
return (string[])HttpContext.Current.Cache["SiteThemes"];
}
else
{
string themesDirPath =
HttpContext.Current.Server.MapPath("~/App_Themes");
// get the array of themes folders under /app_themes
string[] themes = Directory.GetDirectories(themesDirPath);
for (int i = 0; i <= themes.Length - 1; i++)
themes[i] = Path.GetFileName(themes[i]);
// cache the array with a dependency to the folder
CacheDependency dep = new CacheDependency(themesDirPath);
HttpContext.Current.Cache.Insert("SiteThemes", themes, dep);
return themes;
}
}
}
}
完成了控件后,打开模板页面。在Source视图中,向文件的顶端添加一行代码以引用外部用户控件,如下所示:
<%@ Register Src="Controls/ThemeSelector.ascx" TagName="ThemeSelector"
TagPrefix="mb" %>
然后在该控件应该出现的地方(也就是themeselector容器中)声明一个控件实例。



处理主题切换的代码不能放在DropDownList的SelectedIndexChanged事件中,因为该事件在页面生命周期很晚时才发生。就像在"解决方案"部分所提到的,新主题必须放在页面的PreInit事件中。这样,只需要在基类中写一次代码就行了,不需要对每个页面都重写。我们的目的是在自定义基类中读取DropDownList选定项的索引值,然后使用在DropDownList中选定的主题。然而,在PreInit事件中无法访问控件和它们的值,因为PreInit事件发生得太早。所以,只能在晚一点发生的服务器事件中读取这些值。Load事件很适合,但在该事件处理程序中无法得到DropDownList 控件的ID,因此需要一个能够识别该控件的方法,然后就可以通过回送给服务器的行数据(Request.Form集合)将值读出来。但还存在一个问题:必须知道控件的ID才能从集合中读取值,而ID可能会因为放置控件的容器而有所不同。对控件硬编码也不是好的办法,因为以后可能会改变控件的位置。我们可以在一开始创建控件时,把它的客户端ID保存在一个类的某个静态字段中。这样,在整个程序运行周期都可以对该值进行维护,直到程序停止运行(更精确地说,是直到程序集的应用域被卸载)。因此要在App_Code 文件夹中添加一个名为Globals.cs的文件,并在该文件中添加如下代码: namespace MB.TheBeerHouse
{
public static class Globals
{
public static string ThemesSelectorID = "";
}
}
然后,返回到ThemeSelector的后台代码文件中,添加一段代码以在静态字段中保存它的ID:
public partial class ThemeSelector : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (Globals.ThemesSelectorID.Length == 0)
Globals.ThemesSelectorID = ddlThemes.UniqueID;      ddlThemes.DataSource = Helpers.GetThemes();
ddlThemes.DataBind();      ddlThemes.SelectedValue = this.Page.Theme;
}
}
现在准备为页面创建自定义基类,这是另外一个放在App_Code中的普通类,它继承于System.Web.UI.Page。重写该类的OnPreInit方法,完成下列任务:
1. 检查当前的请求是否是为回送,如果是,再检查它是否由ThemeSelector下拉列表触发。就像在ASP.NET 1.x中一样,所有包含服务器端表单的页面都有一个名为EVENTTARGET的隐藏字段,其中保存了触发回送(如果不是Submit按钮)的HTML控件的ID。若要进行验证,可以检查Form集合中的__EVENTTARGET元素中是否包含了该下拉列表的ID,ID从Globals 类中读取。
2. 如果任务1中的条件都满足,则需要使用Globals中保存的ID访问Form元素集合,从而获得选定主题的名称,并用它来设置页面的Theme属性。然后,把该值保存到一个Session变量中,这样用户以后请求时就可以正确地加载新选定的主题,不需要重置为默认主题。
3. 如果当前的请求不是回送,则检查任务2中使用的Session变量是否为空。如果不是,则得到它的值并使用该值来设置页面的Theme属性。
上述任务的实现代码如下:
namespace MB.TheBeerHouse.UI{
public class BasePage : System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
string id = Globals.ThemesSelectorID;
if (id.Length > 0)
{
// if this is a postback caused by the theme selector's dropdownlist,
// retrieve the selected theme and use it for the current page
request
if (this.Request.Form["__EVENTTARGET"] == id &&
!string.IsNullOrEmpty(this.Request.Form[id]))
{
this.Theme = this.Request.Form[id];
this.Session["CurrentTheme"] = this.Theme;
}
else
{
// if not a postback, or a postback caused by controls other then
// the theme selector, set the page's theme with the value found
// in Session, if present
if (this.Session["CurrentTheme"] != null)
this.Theme = this.Session["CurrentTheme"].ToString();
}
}
base.OnPreInit(e);
}
}
}
注意:
这里将选定的主题保存在一个会话变量中,当会话结束时,即浏览者关闭浏览器,或者是20分钟内没有任何页面请求时(时间长短可以自定义),会话变量的值会被清空。使用Profile属性会更合适,它有很多优点并且变量值可以在不同的会话中保持,本书将在第4章中介绍这个新功能,并且使用它对这段代码进行修改。
最后是对Default.aspx 页面默认的后台代码类进行修改,这样就可以使用自己的BasePage 类替换掉默认的Page类。然后在自定义的基类中调用原来的Page类。完成这些只需要改动一个地方(将Page 改为BasePage)即可,如下所示:
public partial class _Default : MB.TheBeerHouse.UI.BasePage
{
protected void Page_Load(object sender, EventArgs e)
{ }
}
现在已经完成了修改,可以运行该项目了。默认情况下看到的主页如图2-4所示(登录框中至今还没有任何内容,在第5章中完成它),这时用的是TemplateMonster主题。从ThemeSelector下拉列表中选择PlainHtmlYellow项,主页就会产生变化,如图2-6所示。