| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1098 人关注过本帖
标题:转载几篇很帅的文章
只看楼主 加入收藏
hebingbing
Rank: 6Rank: 6
来 自:黄土高坡
等 级:贵宾
威 望:27
帖 子:3417
专家分:371
注 册:2007-10-22
结帖率:84.21%
收藏
 问题点数:0 回复次数:7 
转载几篇很帅的文章
分享一下学习Google App Engine的学习心得,整个文章包括以下部分:
   
  ·Google App Engine前言介绍
  ·开发环境配置
  ·起步,写个Hello, World!进行测试
  ·App Engine的对象实体,使用Datastore API添加并显示留言内容
  ·使用Users API,创建用户登录和管理员身份识别,回复留言、删除留言
  ·让你的程序更健壮,捕捉处理异常和错误
  ·激动人心的地方,使用模板!
  ·如何在程序中使用静态文件,如CSS、脚本、图片、视频等。
  ·将程序上传到Google App Engine的免费测试服务器http://application-id.
   
  这是文章的第一部分
   
  前言:什么是Google App Engine?
   Google App Engine是一个托管的服务,允许你本地使用Google基础设施构建Web应用,待其完工之后再将其部署到Google基础设施之上(Google的服务器环境)。Google App Engine分为免费版和收费版,免费版提供了一个配额系统,它限制了应用免费可用的存储、CPU和带宽。目前免费版的配额包括:3个应用/开发者、500MB存储/应用、2000封邮件/天(连续24小时)、10 GB入站带宽、10 GB出站带宽、200M CPU兆周、650k HTTP请求、2.5M Datastore API调用和160k URL Fetch API调用。这对我们来说足可以创建一个小型的、学习试验目的程序了。
   目前Google App Engine提供的语言支持仅为Python,未来可能会增加更多语言支持(估计短期不太可能)。
  Google App Engine SDK可以看作是类似.NET Framework一样的平台(这个比喻不太恰当,但你可以先这样理解),这个环境运行在Google的服务器上,Google对它的安全、性能等作了多种限制和调节。另外Google App Engine是有选择性的支持Python,你不可能用到Python的全部特性。
   
  关于Google App Engine的更多信息可以了解Google推出Web开发利器:AppEngine
   
   
   
  开发环境配置(本文描述的系统环境为windows系统):
   
  1、Google App Engine是运行在Python2.5基础上的,如果没有安装Python 2.5,请从Python的官方网站(http://www.)下载和安装对应操作系统的版本。
  2、下载App Engine SDK(http://code.)。截至写文章之前,SDK的最新版本是1.1,下载后运行安装程序,按照提示操作即可。
  3、有两个重要的命令帮助我们处理操作程序
  ·dev_appserver.py,Web服务器开发
  (http://code.
  ·appcfg.py,用来上传你的程序到App Engine
  (http://code.
  正常安装SDK,Windows安装程序会把这些命令直接放到命令路径中。安装完成SDK之后,你可以直接从命令行窗口下运行这些命令。
   
   
  起步,写个Hello, World!进行测试
   
  1、创建一个名为helloworld的目录。本程序提到的所有文件都放置在这个目录中。
  进入到helloworld目录中,创建一个名为helloworld.py的文本文件,并添加以下代码:
   
  # -*- coding: utf-8 -*-
  print 'Content-Type: text/plain'
  print ''
  print 'Hello, world!'
  这个Python脚本响应一个请求,这个响应包括描述内容类型的HTTP头、一个空行
  以及信息“Hello world!”。
   
  2、创建配置文件
  App Engine应用程序有一个名为app.yaml的配置文件。此文件描述了哪个处理器(handler)脚本对应哪个URL。在helloworld目录中,创建一个名为app.yaml的文件,并添加以下内容:
   
  application: helloworld
  version: 1
  runtime: python
  api_version: 1
   
  handlers:
  - url: /.*
   script: helloworld.py
   
  这个文件的语法是YAML(http://www.)。若要查看完整的配置选项列表,请查看app.yaml参考(http://code.)。
  可以把这个文件当成的web.config文件,主要配置程序一些必要的信息。
   
  3、测试
   在桌面点击开始->附件->命令提示符,在命令提示符状态下进入使用下列命令启动Web服务器,路径为helloworld的目录:
  dev_appserver.py helloworld/
   注意:是目录外,而且目录名不能包含空格
   正常情况下,这个Web服务器开始运行,可以监听8080端口的请求。通过在浏览器中访问下面的地址来测试这个程序。http://localhost:8080/,如果一切正常,浏览器中会显示出Hello world!的字样。你可以编辑helloworld.py文件,将Hello, world!替换成其它字符,刷新浏览器即可看到变化。若要关闭Web服务器,在终端窗口激活的情况下,按下Control-C(或Control-Break)。或者直接关掉窗口。
   
   
  接下来我们开始实现写一个留言板程序
   
  1、创建一个目录,名字为messageboard,创建一个app.yaml文件,添加以下内容:
   
  application: messageboard
  version: 1
  runtime: python
  api_version: 1
   
  handlers:
  - url: /
   script: messageboard.py
   
   
  2、创建一个messageboard.py,添加以下内容:
   
  # -*- coding: utf-8 -*-
   
  import os
  import re
  import cgi
  import datetime
  import wsgiref.handlers
  from google.appengine.ext import db
  from google.appengine.api import users
  from google.appengine.ext import webapp
  from google.appengine.ext.webapp import template
   
   
   
   # -*- coding: utf-8 -*-是告诉python,这个文件格式为utf-8(保存文件时也要选择保存为utf-8),import语句相当于 的using,将需要的类库导入到程序中,其中以google.*开头的是Google App Engine SDK里面的库。具体import和from的区别可参考python的文档。
   
  3、继续,创建一个实体类,将以下代码添加至messageboard.py
   
  class Message(db.Model):
   title=db.StringProperty()
   nickname=db.StringProperty()
   email=db.EmailProperty()
   website=db.LinkProperty()
   content=db.StringProperty(multiline=True)
   reply=db.StringProperty(multiline=True)
   ipaddress=db.StringProperty()
   adddate=db.DateTimeProperty(auto_now_add=True)
   
   这个类定义了一个留言信息的Model,诸如留言标题、昵称、电子邮件等属性,其中的db.StringProperty()之类的代码是指定了属性的数据类型,db.StringProperty()代表这个属性是个字符串,db.StringProperty(multiline=True)是一个带有multiline=True参数的db.StringProperty类型,指定属性可以存储多行文本。db.EmailProperty()和db.LinkProperty()是SDK为我们定义好的属性类型,指定属性必须为Email格式和超链接格式,带有auto_now_add=True参数的db.DateTimeProperty类型指定了当对象被创建时,如果应用程序没有赋予其他值,那么新的对象自动被赋予一个date的时间。更多的信息可以参考(http://code.
   
  4、实现添加留言的功能,将以下代码添加至messageboard.py:
   
  class MainPage(webapp.RequestHandler):
   def get(self):
   self.response.out.write("""
   <form action="/sign" method="post">
   <div><input type="text" name="title"/></div>
   <div><input type="text" name="nickname"/></div>
   <div><input type="text" name="email"/></div>
   <div><input type="text" name="website"/></div>
   <div><textarea name="content" rows="3" cols="60"></textarea></div>
   <div><input type="submit" value="Sign Guestbook"></div>
   </form>
   </body>
   </html>""")
   
  class Add(webapp.RequestHandler):
   def post(self):
   message = Message()
   message.title = self.request.get('title')
   message.nickname = self.request.get('nickname')
   message.email = self.request.get('email')
   message.website = self.request.get('website')
   message.content = self.request.get('content')
   message.put()
   self.redirect('/')
   
  def main():
   application = webapp.WSGIApplication([('/', MainPage),('/sign', Add)],debug=True)
   wsgiref.handlers.CGIHandler().run(application)
   
  if __name__ == "__main__":
   main()
   
   
   这次添加的代码稍多一些,主要是两个类,一个是MainPage用来显示留言的表单,程序是直接将硬代码write到页面了;Add类用来将提交的表单存储到数据库中。message.put()的作用就是保存我们的Message对象到数据库中,并转向到起始页。
   还有一个函数main用来接受请求并指定请求匹配的处理器(这里指的就是MainPage类和Add类),例如,当webapp接收到一个HTTP GET对URL“/”的请求时,它会实例化MainPage类,并调用这个实例的get方法。参数debug=true可以捕捉错误或异常并且显示出来。
   应用程序本身由webapp.WSGIApplication实例来呈现,代码使用了来自Python标准库的wsgiref模块来运行WSGIApplication,即一个CGI适应器。想要了解这个模块更多的信息,请访问wsgiref模块文档(http://docs.)。了解webapp的更多信息,请查看webapp参考(http://code.)。
   
  5、接下来我们再修改一下app.yaml文件
   
  application: messageboard
  version: 1
  runtime: python
  api_version: 1
   
  handlers:
  - url: /
   script: messageboard.py
   
  - url: /add
   script: messageboard.py
   
   现在就可以运行dev_appserver.py messageboard/ 进行调试了。在浏览器中输入http://localhost:8080/,可以看到留言表单,输入一些内容测试。点击提交按钮并没有出现什么变化,实际上数据已经存储了。开发Web服务器使用一个本地版本的数据库来测试你的应用程序,即使用临时文件。只要临时文件错在数据就会一直存在,Web服务器不会重置这些文件
   除非你要求这么做。如果你想要开发服务器在启动时预先清除数据库,请在启动服务器时添加--clear_datastore选项:
  dev_appserver.py --clear_datastore messageboard
   
  6、那么,如何将存储的数据显示出来呢?修改你的MainPage类如下:
   
  class MainPage(webapp.RequestHandler):
   def get(self):
   messages = db.GqlQuery("SELECT * FROM Message ORDER BY adddate DESC LIMIT 10")
   for message in messages:
   self.response.out.write('%s于%s留言:'%(message.nickname,message.adddate))
   self.response.out.write('<hr/>邮件:%s<br/>网址:%s<br/>留言内容:<blockquote>%s</blockquote>' %(message.email,message.website,cgi.escape(message.content)))
   self.response.out.write("""
   <form action="/add" method="post">
   <div>标题:<input type="text" name="title"/></div>
   <div>昵称:<input type="text" name="nickname"/></div>
   <div>邮件:<input type="text" name="email"/></div>
   <div>网址:<input type="text" name="website"/></div>
   <div>内容:<textarea name="content" rows="3" cols="60"></textarea></div>
   <div><input type="submit" value="提交"></div>
   </form>
   </body>
   </html>""")
   
   
   看看这句messages = db.GqlQuery("SELECT * FROM Message ORDER BY adddate DESC LIMIT 10"),Google App Engine使用与sql语句相似的称为gql的语句进行数据库查询,这个查询返回添加的信息的Message对象模型。接下来
  for message in messages:
   self.response.out.write('%s于%s留言:'%(message.nickname,message.adddate))
   self.response.out.write('<hr/>邮件:%s<br/>网址:%s<br/>留言内容:<blockquote>%s</blockquote>' %(message.email,message.website,cgi.escape(message.content)))
  用于遍历这个messages对象集合,并将内容write出来。。
   GQL和SQL相似度较高,其中GQL增加了一些特殊语法,已适应App Engine的环境,查看完整的GQL及查询API描述,请参见数据存储参考(http://code.)。
   
   调试一下我们的程序,如果测试服务器没有关闭的话,刷新一下浏览器中(http://localhost:8080/)即可看到刚刚提交的留言,你可以尝试继续提交留言并查看变化。
   注意,当填写的Email和Url不符合标准的话,测试服务器会捕捉到错误并显示出来,例如:BadValueError: Invalid URL: ww.124.cm 说明添加的网址格式错误,我们可以根据此机制来编写处理代码,让我们的程序更健壮、更安全。
   
   通过以上的学习,初步构建了一个可以运行在本地App Engine环境的程序,当然,它还很稚嫩,程序不健壮、界面不友好,没有区分用户身份,无法对存储数据管理等等。下篇文章将帮你解决这些问题,下篇文章预告:
   
  ·使用Users API,创建用户登录和管理员身份识别,回复留言、删除留言
  ·让你的程序更健壮,捕捉处理异常和错误
  ·激动人心的地方,使用模板!
  ·如何在程序中使用静态文件,如CSS、脚本、图片、视频等。
  ·将程序上传到Google App Engine的免费测试服务器http://application-id.
   完整代码下载
搜索更多相关主题的帖子: 1111 
2008-06-28 17:21
hebingbing
Rank: 6Rank: 6
来 自:黄土高坡
等 级:贵宾
威 望:27
帖 子:3417
专家分:371
注 册:2007-10-22
收藏
得分:0 
asp.net中使用多线程和ajax解决长时间执行任务
一个应用中,可能我们有时候会需要处理一个可能长时间执行的任务。比如说将新闻内容生成静态页面,如果在中调用方法来处理,很可能引起超时操作,而且我们也无法获知当前处理的进度情况。那么我们应该如何解决该问题呢?答案是使用Thread+AJAX。

      基本的解决办法是,首先我们将可能长时间执行的程序放在一个Thread中执行:
Thread tr= new Thread(new ThreadStart(ArticleCreate));
tr.Start();

      然后在执行过程中将执行的进度数据存在Session中:
private void ArticleCreate()
{
    string sqlStr="select * from article order by id asc"
    DataSet ds=……
    int tCount =ds.Tables[0].Rows.Count;
    for(int i=0;i<tCount;i++)
    {
        ……
        Session["c_pro"] = (i+1)*100/tCount;
    }
    Session["c_pro"]=100;
}

      再后,我们使用从客户端读取服务器端的Session值:
[AjaxMethod(AjaxPro.HttpSessionStateRequirement.Read)]
public int tmethod()
{
    return (int)Session["c_pro"];
}

注意红色字体部分,这是必要的参数,否则无法在AjaxMethod中使用Session,进而在客户端也将无法调用。
2008-06-28 17:22
hebingbing
Rank: 6Rank: 6
来 自:黄土高坡
等 级:贵宾
威 望:27
帖 子:3417
专家分:371
注 册:2007-10-22
收藏
得分:0 
Web站点风格切换的实现
引言
Web站点的风格切换是很常见、也很受大家欢迎的功能,比如大家熟知的博客园就提供了几十款风格模板供大家选择。在中,我们可以通过模板页master page和主题theme来实现网站的风格切换,但是.Net提供的默认设置不够强大和灵活。本文将向大家介绍如何在.Net提供的方法上进行改进和扩展,以提供更加强大的网站风格切换功能。

效果预览:http://www.

NOTE:本文将master page称为模板(有的书上叫母版),将theme称为主题。

网页的结构 和 模板、主题配置的局限
静态网页的结构
网站的风格的切换,说白了,实际上就是对页面进行分解和重组。所以在进行之前,我们先简单回顾看一下页面究竟有哪些组成部分可以供我们分解,分清楚哪些是可变的、哪些是不变的,进行后继的工作才会容易得多。现在我们先暂时脱离服务器端,看一下一个静态的.htm页面由哪几部分组成:

结构(有语义的XHTML):这部分由XHTML标记组成,应该注意,这里使用“有语义”三个字作为修饰。XHTML的职责是告诉“这里是什么”,而不是告诉“这里应该如何显示”。尽管浏览器对于几乎每个XHTML的标记都赋予了某种内置的样式控制,但是XHTML的本意只是规范文档的结构。比如h1表示为标题,p表示为段落。而不是为了这个字显示的更大一些才去使用h1。

表现和布局(CSS):CSS用来控制页面的显示及布局。在Web标准的概念普及以前,我想大多人都是表格套表格来布局的,现在基本都在使用CSS了,这里没有太多好说的。

行为(Javascript):静态网页也可以添加一些交互的行为,这些行为由Javascript来完成。我们时常会把onclick="alert('hello')"这样的代码嵌入到XHTML标记中,比如一个Input标记上;一些结构、行为分离的狂热人士则主张将行为与结构(XHTML)分离,他们不会将javascript代码写到<body>之间,而全部写在了head中,或者是body下面,使用 window.onload=function(){ // …} 这种形式。有个极力主张这种做法的人(Peter-Paul Koch)写了本书叫《ppk on javascript》。

好了,大概了解了这些,我们看下.Net中如何将这三者分离,以及它的一些限制:

.Net对页面结构的分离
我到现在都觉得 行为与结构 完全分离的概念太前卫了,另外它也不影响对网站的风格设置,所以我们这里不讨论它。

我们先看一下结构:现在我们将思维向服务器端靠拢一下,很快会发现上面的结构部分仍需要再细分一次,就是XHTML标记和标记中的内容(网页内容)分离。XHTML标记属于变化的部分,不同的风格可能会需要不同的XHTML结构,而对于各个风格,它所显示的内容显然是一样的。想要得到这样的效果,我们可以使用Master Page模板页。由Master Page模板页对应XHTML结构(变化部分),由Page页面对应于XHTML页面的内容(不变部分),即是一个Page可以设置为不同的Master Page以达到不同的样式(look and feel)。

NOTE:这里在说一下CSS,如果你的CSS水平足够强,XHTML的代码写得足够好,那么你无需搞得这么复杂,仅仅使用CSS就可以实现换肤了。www.有这样的一个项目提供给全世界的人作为实践,它提供一套统一的XHTML代码,其他人则自己编写CSS来对这个XHTML代码进行样式化,结果是百花齐放,一模一样的XHTML实现了风格迥异的页面设计。

现在在看一下表现部分,表现层分为全局式的CSS以及基于控件的皮肤Skin,这些都可以交由主题Theme来完成。

.Net 设置上的局限
看到这里,你可能觉得不用往下看了,使用Master Page和Theme谁不会啊。现在我们就讨论下.Net 中Master Page 和 Theme 的局限:

大家知道,我们可以在 Web.config中的System.Web结点下的pages结点中添加Theme和masterPageFile属性来对网站进行设置。但是它提供了一个全局性的配置,就是对于整个站点的所有页面,都将使用这个Master Page。而有时候我们希望能够每个页面不相同,这里就无法实现了。虽然我们可以通过使用location结点来为各个页面进行单独设置,但是显然太麻烦了。
我们希望每个风格都有个名字,比如说“默认风格”、“春意盎然”。
我们希望用户可以选择风格,而不是像博客园这样由博主设置风格。
实现网站的风格切换
自定义风格配置
有了思路后我们就来一步步地实现它,我们希望可以对风格进行简单的设置,我们应该先明确需要设置的内容:我们都有哪些风格、当前使用的风格是什么、每个风格使用了什么主题、哪个页面对应哪个模板。了解了这些之后,我们可以写下这样的结点配置来:

<!-- MasterPage的 Path为 masterRoot + theme + master -->
<!-- 不为Default设置masterPage,
     使用Default风格时会使用创建页面时所选择的Master Page -->
<styleTemplates default="默认风格" masterRoot="~/MasterPage">
     <style name="默认风格" theme="Default" ></style>
     <style name="春意盎然" theme="Spring" >
          <masterPages>
               <page path="/website/default.aspx" master="Default.master" />
               <!--<page path="/other.aspx" master="Default.master" />-->
               <page path="/default.aspx" master="Default.master" />
          </masterPages>
     </style>
</styleTemplates>

styleTemplates是风格设置的根节点,default是默认的风格名称;masterRoot是指模板页的根目录的地址;style结点是各个风格的名称,以及其所使用的主题的名称;style结点下的masterPages结点组包含此风格所使用的模板的信息;其中Page子结点对应每个独立的页面,path属性记录的是页面的路径,master属性是每个页面对应的模板的路径。

page结点的master属性没有给出模板的全路径,这么做一个是为了使目录结构更一目了然,还有就是不想master属性的内容变得很长,所以需要在程序中指定页面的模板的路径为 masterRoot + Theme + masterPage名称。这样在模板的根目录下,必须建立与主题同名的目录,然后将模板位于该目录下。

在创建页面时,我们需要要选择一个模板。注意到这一点非常重要,因为这个模板实际上是该页面的默认模板。当我们在Web.config中将相应风格的page结点留空时(没有对此页面设置模板),那么就会应用这个模板。所以在Web.Config中定义风格的模板,只是覆盖了这个我们在创建页面时选择的模板。由此,我们只用定义一套完整的Master Page模板页,供每个页面在创建时选择。而Web.config风格结点下设置的master page,实际上仅仅是动态地覆盖这些模板。

以上面的配置为例:当我们将 “春意盎然” 下的第二个 page 结点注释掉时,它会应用创建页面时所选择的模板。“默认风格”下面没有设置任何的 page 结点,所以对于该风格的每个页面,都会应用创建页面时所选择的模板。

如果你希望仅使用Css来换肤,你可以使用也可以不使用Master Page,那么在Web.Config中可以像下面这样设置:

<styleTemplates default="默认风格" masterRoot="">
     <style name="默认风格" theme="Default" ></style>
     <style name="春意盎然" theme="Spring" ></style>
     <style name="秋高气爽" theme="Autumn" ></style>
</styleTemplates>

对于上面使用Master Page时的设置,站点的目录结构如下所示:
" border="0" />



可以看到模板页的根目录下包含了目录Default、Spring与主题名称相同(必须)。之后我们要编写结点处理程序,如何编写自定义结点处理程序,我在《.Net 自定义应用程序配置》一文中已经详细的讨论了,所以这里我们直接看实现,在AppCode目录中新建一个文件StyleTemplateConfigHandler.cs:

public class StyleTemplateConfigHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, XmlNode section) {
       return new StyleTemplateConfiguration(section);
    }
}

// 映射 styleTemplates 结点的实体类
public class StyleTemplateConfiguration {

    private XmlNode node;           // styleTemplates 结点
    private string defaultTheme;    // 默认的主题名称
    private string defaultStyle;    // 站点默认风格名称
    private HttpContext context;
    private Dictionary<string,string> styleDic; // Key: 风格名称;Value: 主题名称


    public StyleTemplateConfiguration(XmlNode node) {
       this.node = node;
       context = HttpContext.Current;
       styleDic = new Dictionary<string, string>();

       // 获取所有style结点的 name属性 和 theme属性
       XmlNodeList styleList = node.SelectNodes("style");
       foreach (XmlNode style in styleList) {
           styleDic[style.Attributes["name"].Value] = style.Attributes["theme"].Value;
       }

       // 获取 站点默认风格 名称
       defaultStyle = node.Attributes["default"].Value;

       // 根据 风格名称 获取主题
       defaultTheme = styleDic[defaultStyle];
    }

    // 获取所有风格名称
    public ICollection<String> StyleNames {
       get {
           return styleDic.Keys;
       }
    }

    // 根据风格名称获取主题名称
    public string GetTheme(string styleName) {
       return styleDic[styleName];
    }

    // 设置、获取 站点默认风格
    public string DefaultStyle{
       get {
           return defaultStyle;
       }
       set { // 更改Web.Config中的默认风格,一般为站长才可以使用
           XmlDocument doc = new XmlDocument();
           doc.Load(context.Server.MapPath(@"~/web.config"));
           XmlNode root = doc.DocumentElement;
           XmlNode styleTemp = root.SelectSingleNode("styleTemplates");

           styleTemp.Attributes["default"].Value = value;
           doc.Save(context.Server.MapPath(@"~/web.config"));
       }
    }

    // 获取默认主题名称
    public string DefaultTheme {
       get { return defaultTheme;   }
    }
   
    // 根据页面路径获取其对应的 masterPage 的路径
    public string GetMasterPage(string userTheme){

       // 获取当前页面路径
       string pagePath = context.Request.Path;

       // 用于定位page结点的 XPath
       string xpath = "style[@theme='" + userTheme + "']" + "/masterPages/page[@path='" + pagePath.ToLower() + "']";

       // 获取与path属性相匹配的page结点
       XmlNode pageNode = node.SelectSingleNode(xpath);

       string master;
       if (pageNode != null) {
           // 获取page结点的 master属性的值
           master = pageNode.Attributes["master"].Value;
           return prepareMasterPath(master, userTheme);
       } else
           return null;
    }
   
    // 获取 Master Page 的路径
    // MasterPagePath = 跟路径 + Theme路径 + 模板路径
    private string prepareMasterPath(string masterPath, string userTheme) {
       string path;

       if (node.Attributes["masterRoot"] != null)
           path = node.Attributes["masterRoot"].Value + "/" + userTheme + "/" + masterPath;
       else {
           if (userTheme != null) {
              path = "~/" + userTheme + "/" + masterPath;
           } else {
              path = "~/" + masterPath;
           }
       }
       return path;
    }
}

这个类提供了一些简单的对XmlNode的操作,对styleTemplates结点进行了映射,这里需要明确两个概念:默认风格 和 用户风格:

默认风格,指的是站点管理员 或者 博主设置的风格,也就是Web.Config 中styleTemplates结点的Default属性。
用户风格,用户设置的风格,页面的实际显示是根据用户风格而 不是默认风格。当用户没有设置风格时,才显示默认风格。
很显然,这个类处理的所有的均是默认风格,我们来看一下它的几个主要方法和属性:

// 获取所有风格名称
public ICollection<String> StyleNames { get {;}   }

// 根据风格名称获取主题名称
public string GetTheme(string styleName) {}

// 设置、获取 站点默认风格
public string DefaultStyle{}

// 获取默认主题名称
public string DefaultTheme { }
   
// 根据页面路径获取其对应的 masterPage 的路径
public string GetMasterPage(string userTheme){}

IUserStyleStrategy,获取、设置用户风格
在继续进行之前,我们来考虑这样一个问题:因为我们要根据用户选择的风格来动态地为页面加载主题和模板,那么用户信息(用户选择了什么风格)应该保存在哪里,从哪里获得呢?我们有很多的选择:可以使用Session、可以使用Cookie,还可以保存到数据库中。此时最好将这部分抽象出来,以便日后为不同的方法提供实现。我们定义一个接口 IUserStyleStrategy,它用来定义如何获取、设置用户风格,在AppCode中新建文件IUserStyleStragety.cs:

public interface IUserStyleStrategy{
    void ResetUserStyle(string styleName);  // 重新设置用户风格
    string GetUserStyle();                     // 获取用户风格
}

然后我在这里提供了一个基于Cookie的默认实现:

// 默认风格设置方法:使用Cookie记录
public class DefaultStyleStrategy : IUserStyleStrategy {
   
    private string cookieName;          // cookie名称
    private HttpContext context;

    public DefaultStyleStrategy(string cookieName){
       this.cookieName = cookieName;
       context = HttpContext.Current;
    }

    // 重新设置用户风格名称
    public void ResetUserStyle(string styleName) {
       HttpCookie cookie;
       if(context.Request.Cookies[cookieName]!=null)
           cookie = context.Request.Cookies[cookieName];
       else
           cookie = new HttpCookie(cookieName);

       cookie.Value = context.Server.UrlEncode(styleName);
       cookie.Expires = DateTime.Now.AddHours(2); // 设置Cookie过期时间

       context.Response.Cookies.Add(cookie);

       // 因为风格(master page和theme)的动态设置只能在 PreInit 事件中
       // 而Button的Click事件在PreInit事件之后,所以需要Redirect才可以生效
       context.Response.Redirect(context.Request.Url.PathAndQuery);
    }

    // 获取用户自己设置的风格名称
    public string GetUserStyle() {      
       if (context.Request.Cookies[cookieName] != null) {
           string value = context.Request.Cookies[cookieName].Value;
           value = context.Server.UrlDecode(value);   // 避免出现中文乱码
           return value;
       } else
           return null;
    }
}

如果日后你需要将信息保存在数据库中,那么你只要重新实现一下这个接口就可以了:

// 如果将用户风格设置存储到了数据库中,可以实现这个接口
public class SqlStyleStrategy : IUserStyleStrategy {
   
    private int userId;          // 用户Id

    public SqlStyleStrategy(int userId){
       this.userId = userId;
       // ...
    }

    public void ResetUserStyle(string styleName) {
       throw new NotImplementedException();
    }

    public string GetUserStyle() {
       throw new NotImplementedException();
    }
}

PageBase 类:继承自Page的基类
因为所有的页面都要运行这样的一个逻辑:判断用户是否有设置风格,如果没有,使用Web.Config中设置的默认风格;如果设置了,使用用户风格。最后动态地给Page类的Theme属性和MasterPageFile属性赋值。

那么我们可以定一个基类,在这个基类中去做这件事,然后让所有的页面继承这个基类;又因为页面是一定要继承自System.Web.UI.Page类的,所以这个基类也必须继承自System.Web.UI.Page。现在在AppCode中再建一个文件 PageBase.cs:

// 供所有基类继承
public class PageBase : Page
{
    protected StyleTemplateConfiguration styleConfig;
    protected string userStyle;         // 用户设置的风格
    protected string userTheme;         // 用户设置的主题
    protected IUserStyleStrategy userStrategy; // 使用何种算法来获得用户自定义的信息
           
    protected override void OnPreInit(EventArgs e) {
       base.OnPreInit(e);

       // 这里会被缓存只在第一次时调用有用
       this.styleConfig = (StyleTemplateConfiguration)ConfigurationManager.GetSection("styleTemplates");

       // 当你实现了自己的 Strategy时只需要更改这里就可以了
       // 更好的办法是将Stragey的类型保存在Web.config中,
       // 然后使用反射来动态创建
       userStrategy = new DefaultStyleStrategy("userStyle");

       // 获取用户风格
       userStyle = userStrategy.GetUserStyle();

       // 如果用户没有设置风格,使用默认风格
       if (String.IsNullOrEmpty(userStyle)) {
           userStyle = styleConfig.DefaultStyle;
           userTheme = styleConfig.DefaultTheme;
       } else {
           // 根据用户设置的风格 获取 主题名称
           userTheme = styleConfig.GetTheme(userStyle);
       }
              
       // 根据当前页获取MasterPage的路径
       string masterPagePath = styleConfig.GetMasterPage(userTheme);

       // 设置当前页的MasterPage
       if (masterPagePath != null)
           this.MasterPageFile = masterPagePath;

       this.Theme = userTheme;      // 设置当前页的主题
    }
}

这里需要注意:

ConfigurationManager.GetSection()方法只会调用一次,然后会将结果进行缓存;只要Web.Config不做修改,以后不管是Request还是PostBack都不会再重新生成StyleTemplateConfig的类型实例,而会从缓存中读取。我在《.Net 自定义应用程序配置》中忘记说了。
userStrategy = new DefaultStyleStrategy("userStyle");这里可以将IUserStyleStrategy的类型信息保存在Web.config中,然后使用反射动态创建。具体方法依然参见《.Net 自定义应用程序配置》。
如果想动态地为页面设置主题和模板,代码必须写在PreInit事件中。参见《 Page Life Cycle Overview》。
效果预览
因为这只是一个范例程序,我主要是表达实现的思路,而不是代码的编写,所以省略了很多诸如结点属性是否为空之类的判断。下面的测试仅仅在Web.Config中的配置正确时。在站点下新建一个页面,比如Default.aspx,注意创建一个模板页,因为这里设置的是会被覆盖的,所以无所谓选择哪个模板。

添加App_Theme下创建目录Default、Spring,新建一个目录MasterPage,在下面创建目录Default、Spring,然后添加一些文件(这就不用我说了吧)。在页面上添加一个DropDonwList,一个Button,当我们选择要显示的模板时,会进行相应的切换,编写后置代码:

protected void Page_Load(object sender, EventArgs e) {

    if (!IsPostBack) {
       ltrStyleName.Text = userStyle;

       foreach (string styleName in styleConfig.StyleNames) {
           ListItem item = new ListItem(styleName);
           if ((styleName, userStyle) == 0)
              item.Selected = true;
           ddlStyles.Items.Add(item);
       }
    }
}

// 更换风格
protected void Button1_Click(object sender, EventArgs e) {
    string styleName = ddlStyles.SelectedValue;
    userStrategy.ResetUserStyle(styleName); // 委托给userStragety去处理
}

之后你可以看到下面的画面:
" border="0" />
" border="0" />





可以通过下面这个连接来看实际的效果,注意到:在这里我让 Default.aspx 和 Other.aspx 使用了同一个模板,你也可以设置它们使用不同的模板。

http://www.

总结
在这篇文章中,我简单地向大家介绍了实现网站风格切换的一个思路。

我们首先复习了网页的结构,了解了.Net默认配置的不足。接着分三个步骤实现了网站的风格切换:处理配置结点的程序、获取用户风格的方法、以及通过基类继承来为各个页面设置风格。最后我们通过简单的两个页面进行了下测试和预览。
2008-06-28 17:26
雪雨星风
Rank: 1
来 自:北京
等 级:新手上路
威 望:1
帖 子:566
专家分:0
注 册:2007-10-11
收藏
得分:0 
你学的是什么专业?

C#发烧友QQ群1:5953354   QQ群2:68096127  QQ群3:30217249  QQ群4:37023079   为了更好方便大家交流学习,请不要重复加群
2008-06-28 17:30
雪雨星风
Rank: 1
来 自:北京
等 级:新手上路
威 望:1
帖 子:566
专家分:0
注 册:2007-10-11
收藏
得分:0 
北大青鸟的吗?

C#发烧友QQ群1:5953354   QQ群2:68096127  QQ群3:30217249  QQ群4:37023079   为了更好方便大家交流学习,请不要重复加群
2008-06-28 17:31
hebingbing
Rank: 6Rank: 6
来 自:黄土高坡
等 级:贵宾
威 望:27
帖 子:3417
专家分:371
注 册:2007-10-22
收藏
得分:0 
不是,我学的专业是计算机科学与技术,一个软硬兼施的专业,但是自己不喜欢硬件,喜欢软件多一点,所以硬件的课程挂科很严重的。呵呵,目前已经是3门了,还有三年的没有考了。有点怕毕不了业
呵呵……
2008-06-28 17:38
雪雨星风
Rank: 1
来 自:北京
等 级:新手上路
威 望:1
帖 子:566
专家分:0
注 册:2007-10-11
收藏
得分:0 
你太牛了    我学的太少了   动态网站开发我门只开了2个月的课程
1个月的sql

C#发烧友QQ群1:5953354   QQ群2:68096127  QQ群3:30217249  QQ群4:37023079   为了更好方便大家交流学习,请不要重复加群
2008-06-28 17:40
hebingbing
Rank: 6Rank: 6
来 自:黄土高坡
等 级:贵宾
威 望:27
帖 子:3417
专家分:371
注 册:2007-10-22
收藏
得分:0 
看来你是北大青鸟的,呵呵,我们学校也有,但是我没有报。学东西要靠自己
2008-06-28 17:44
快速回复:转载几篇很帅的文章
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.029234 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved