[转载]更灵活,更易维护的WebHandler之通用webHandler编码方案(2)

mikel阅读(885)

[转载]更灵活,更易维护的WebHandler之通用webHandler编码方案(2) – 刘铭.net-OPS.cc – 博客园.

上一篇:更灵活,更易维护的WebHandler之通用webHandler编码方案(1) 中介绍了在同一个程序集中使用webHandler执行类的方法,

但在多数情况下,我们会将WebHandler封装进一个单独的动态链接库,我们需要将引用WebHandler DLL的程序集或程序集中的任意一个类的Type做为参数传递给WebHandler,

这样就可以在WebHandler中利用反射调用引用WebHandler类库的程序集中的代码!

实现代码如下:

01 /* *
02 * name     : ExecuteHandler.cs
03 * author   : newmin
04 * date     : 09/29 2010
05 * note     : 用来处理请求,请求的URI参数如:Exc.ashx?cmd=IP,GetIP,127.0.0.1
06 *
07 * 要执行操作的类必需要程序集名称命名空间下:
08 * 如要执行AtNet.Security下的User类,则User类的命名空间为:HuiShi.Security.User
09 * 调用方式**.ashx?cmd=User,GetScore,newmin
10 *
11 * */
12 namespace AtNet.Web
13 {
14 using System;
15 using System.Web;
16 using System.Reflection;
17 using System.Collections.Generic;
18
19 public abstract class ExecuteHandler : IHttpHandler
20 {
21 //绑定类型用于获取程序集,只能在子类的静态构造函数中赋值
22 protected static Type _type;
23 #region IHttpHandler 成员
24 public bool IsReusable{ get; set; }
25
26 public void ProcessRequest(HttpContext context)
27 {
28 string cmd=context.Request["cmd"].Replace("+"," "); //将空格做为+号替换
29
30 string[] args = cmd.Split(',');
31 if (args.Length > 2)
32 {
33 //获取执行当前代码的程序集并创建实例
34 Assembly ass = Assembly.GetAssembly(_type);
35 object obj = ass.CreateInstance(_type.Namespace+"."+args[0], true);
36
37 //获取实例类型
38 Type type=obj.GetType();
39
40 //未添加WebExecuteAttribute特性的类将不被执行
41 object[] attrs= type.GetCustomAttributes(typeof(WebExecuteAttribute), false);
42 WebExecuteAttribute attr =attrs.Length>0?attrs[0] as WebExecuteAttribute:null;
43 if (attr == null) { context.Response.Write("此模块不允许被执行!"); return; }
44
45 //获取方法并执行
46 MethodInfo method =type.GetMethod(args[1],BindingFlags.Instance|BindingFlags.Public|BindingFlags.IgnoreCase);
47 object returnObj=method.GetParameters() != null?method.Invoke(obj,cmd.Substring(args[0].Length + args[1].Length + 2).Split(','))
48 :method.Invoke(obj, null);
49
50 //如国返回String类型或值类型则输出到页面
51 if (method.ReturnType == typeof(string) ||obj is ValueType)
52 context.Response.Write(returnObj.ToString());
53 }
54 }
55
56 #endregion
57 }
58 }

我们需在继承ExecuteHandler的类的静态构造函数中对_type赋值:

01 namespace AtNet.Web.Tools
02 {
03 using System;
04 using System.Reflection;
05
06 public class WebHandler:AtNet.Web.ExecuteHandler
07 {
08 static WebHandler()
09 {
10 _type = typeof(WebHandler);
11 }
12 }
13 }

这样我们就能在将ExecuteHandler分离出来,被别的项目所引用

[转载]Lucene.Net:构造搜索表达式简化搜索

mikel阅读(1038)

[转载]Lucene.Net:构造搜索表达式简化搜索 – 努力,天赋,笑容,自信 – 博客园.

我们知道利用Lucene.Net的不同的Query(常见如BooleanQuery,RangeQuery等等),可以有针对性地进行各种不同 类型的搜索。利用QueryParser(或MultiFieldQueryParser),配合构造好的搜索关键字(搜索表达式),也可以实现不同类型 的搜索。本文重点就是简单介绍一下搜索表达式和不同类型的Query之间的简单对比。本文最后的demo,QueryApp工程下有文章里贴出的大部分示 例代码,代码自己会说话,有时候它可能更好地表达了文章作者的思路。您可以下载对照着本文进行阅读。

一、与或非

1、与

举例:搜索contents既包含“jeffreyzhao”,又有“ 老赵”的记录:

1 public static void NormalQueryParserTest(Analyzer analyzer, string field, string keyword)
2 {
3 QueryParser parser = new QueryParser(Version.LUCENE_29, field, analyzer);
4 Query query = parser.Parse(keyword);
5 ShowQueryExpression(analyzer, query, keyword);
6 SearchToShow(query);
7 Console.WriteLine();
8 }

调用的时候,我们构造一个搜索关键词“+jeffreyzhao +老赵”:

1 string field = "contents";//搜索的对应字段
2 keyword = "+jeffreyzhao +老赵";
3 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao +contents:"老 赵"

搜索结果中我们可以看到,通过加号(+)可以表达与(AND)的关系(+contents:jeffreyzhao +contents:”老 赵” )。

特点:不同关键字越多,匹配的结果可能越少。

2、或

输入多个关键字,任何包含其中一个关键字的记录都被搜索出来:

1 string keyword = "jeffreyzhao 老赵";//搜索输入关键词
2 string field = "contents";//搜索的对应字段
3 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword); //contents:jeffreyzhao contents:"老 赵"

特点:不同关键字越多,匹配的结果可能越多。

3、非(!)

1 keyword = "+jeffreyzhao -老赵";
2 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao -contents:"老 赵"
3
4 keyword = "+jeffreyzhao !老赵";
5 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao -contents:"老 赵"

上面的两种写法,转换成表达式都是+contents:jeffreyzhao -contents:”老 赵” 。

根据我们的测试结果,与或非的关系可以总结如下:

a & b  =>   +a +b
a || b  =>   a    b
a  !b   =>   +a  -b

这种与或非的关系,我们还可以通过BooleanQuery表达同样的搜索:

01 public static void BooleanQueryTest(Analyzer analyzer, string field, string keyword, BooleanClause.Occur[] flags)
02 {
03 Console.WriteLine("====BooleanQuery====");
04 string[] arrKeywords = keyword.Trim().Split(new char[] { ' ', ',', ',', '、' }, StringSplitOptions.RemoveEmptyEntries);
05 QueryParser parser = new QueryParser(Version.LUCENE_29, field, analyzer);
06 BooleanQuery bq = new BooleanQuery();
07 int counter = 0;
08 foreach (string item in arrKeywords)
09 {
10 Query query = parser.Parse(item);
11 bq.Add(query, flags[counter]);
12 counter++;
13 }
14 ShowQueryExpression(analyzer, bq, keyword);
15 SearchToShow(bq);
16 Console.WriteLine();
17 }

其中BooleanClause.Occur(MUST:+  MUST_NOT:-   SHOULD:无符号)的选择至关重要:

01 string field = "contents";//搜索的对应字段
02 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
03 BooleanClause.Occur[] occurs = new BooleanClause.Occur[] { BooleanClause.Occur.MUST, BooleanClause.Occur.MUST };
04 foreach (Analyzer analyzer in listAnalyzer)
05 {
06
07 //NormalQueryTest(analyzer);
08 //LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//直接通过QueryParser配合构造好的查询表达式搜索
09
10 //LuceneSearch.TermQueryTest(analyzer, field, "高手");//contents:高手
11
12 LuceneSearch.BooleanQueryTest(analyzer, field, "jeffreyzhao 老赵", occurs);//+contents:jeffreyzhao +contents:"老 赵"
13
14 //LuceneSearch.RangeQueryTest(analyzer, rangeField, start, end); // createdate:[20101010 TO 20110101]  createdate:[20101010 TO 20110101}
15
16 //LuceneSearch.PrefixQueryTest(analyzer, field, "hell"); // contents:hell*  (可以找到hello world那一项)
17
18 //LuceneSearch.WildcardQueryTest(analyzer, field, "高手"); //contents:高手
19
20 //LuceneSearch.FuzzyQueryTest(analyzer, field, "牛"); //contents:牛~0.5
21
22 //LuceneSearch.PhraseQueryTest(analyzer, field, "hello world", 1); //contents:"hello world"~1
23
24 //LuceneSearch.MulFieldsSearchTest(analyzer, fieldArr, "博  园", occurs); //+(contents:博 contents:园) +(title:博 title:园)
25 }

二、范围

01 string rangeField = "createdate";//范围搜索对应字段
02 string start = "20101010";
03 string end = "20110101";
04 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
05 foreach (Analyzer analyzer in listAnalyzer)
06 {
07
08 LuceneSearch.RangeQueryTest(analyzer, rangeField, start, end); // createdate:[20101010 TO 20110101]  createdate:[20101010 TO 20110101}
09
10 }

同样道理,RangeQuery(或者TermRangeQuery)也可以实现范围搜索。

三、多字段组合搜索

搜索时,对两个或多个字段进行匹配的时候,可以用下面的方法:

01 public static void MulFieldsSearchTest(Analyzer analyzer, string[] fields, string keyword, BooleanClause.Occur[] flags)
02 {
03 Console.WriteLine("====MultiFieldQueryParser====");
04 MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_29, fields, analyzer);
05 //Query query = parser.Parse(keyword);
06 Query query = MultiFieldQueryParser.Parse(Version.LUCENE_29, keyword, fields, flags, analyzer);
07 ShowQueryExpression(analyzer, query, keyword);
08 SearchToShow(query);
09 Console.WriteLine();
10 }

简单调用如下:

1 string[] fieldArr = new string[] { field, "title" };//两个字段
2 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
3 foreach (Analyzer analyzer in listAnalyzer)
4 {
5 LuceneSearch.MulFieldsSearchTest(analyzer, fieldArr, "博  园", occurs); //+(contents:博 contents:园) +(title:博 title:园)
6 }

如果我们把搜索关键字改为“博 -园”,则表达式就是“+(contents:博 -contents:园) +(title:博 -title:园)”,这也符合单个字段搜索。

注意:如你所知,与或非和范围不是搜索关系的全部。实际上,通过Lucene,你可以根据 +-!():^[]{}~*? 这几种符号,合理构造出表达真实意图的复杂表达式来代替不同类型的Query。我在示例代码中做了几个针对StandardAnalyzer的简单尝试,测试结果符合预期。

我在参考网上不少文章的时候,发现很多提到的问题都没有重现,再看他们的lucene的版本都低于2.0,我大胆猜测Lucene.Net的类库已经改进了不少,一开始还以为自己的测试不到位,囧。

四、分词效果

Analyzer选择不同,搜索结果也不同,尤其是对于中文。用下面的函数可以测试分词效果:

01 /// <summary>
02 /// 测试不同的Analyzer分词效果
03 /// </summary>
04 /// <param name="listAnalyzer"></param>
05 /// <param name="input"></param>
06 public static void TestAnalyzer(IList<Analyzer> listAnalyzer, string input)
07 {
08 foreach (Analyzer analyzer in listAnalyzer)
09 {
10 Console.WriteLine(string.Format("{0}:", analyzer.ToString()));
11
12 using (TextReader reader = new StringReader(input))
13 {
14 TokenStream stream = analyzer.ReusableTokenStream(string.Empty, reader);
15 Lucene.Net.Analysis.Token token = null;
16 while ((token = stream.Next()) != null)
17 {
18 Console.WriteLine(token.TermText());
19 }
20 }
21
22 Console.WriteLine();
23 }
24 }

不同的Analyzer,分词效果可以总结如下:

StandardAnalyzer       对中文单字拆分;

WhitespaceAnalyzer  按空格拆分,对中文的支持不好;

KeywordAnalyzer       输入什么,分词就是什么;

SimpleAnalyzer           按标点和空格拆分,对中文的支持不好

StopAnalyzer               和SimpleAnalyzer类似;

选来选去,StandardAnalyzer 的效果还是很不错的,一般的应用差不多就够用了。 您可以使用不同的Analyzer,然后对比它们的搜索表达式并找出它们的不同之处。

demo下载:LuceneNetApp

[转载]ASP.NET MVC中的扩展点(五)方法选择器

mikel阅读(973)

[转载]MVC中的扩展点(五)方法选择器 – xfrog – 博客园.

前一篇中我们介绍了过滤器,通过方法和结果过滤器我们可以在MVC执行方法及结果的前后注入自己的功能,通过授权过滤器可以执行一些权限检查,阻止无权用 户调用方法,通过异常过滤器处理方法执行过程中产生的异常。那么在执行方法之前,MVC又是如何确定使用何种控制器及其方法的呢?

我们已经知道,MVC使用DefaultControllerFactory控制器工厂来实例化控制器,其大致过程如下:

1、默认Route类的GetRouteData方法将按我们设定的Url规则解析当前请求的Url,并将Url规则中的给个参数存入RouteData.Values集合中。我们知道Mvc添加了一个默认的Route项:

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
 );

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }

按以上规则,如果我们请求的Url为:

http://localhost/News/GetNewsList

则对应的RouteData.Values[“controller”] = “News”;  RouteData.Values[“action”] = “NewsList”

2、DefaultControllerFactory根据Route.Values[“controller”]确定实际的控制器类型,并实例化,如上例控制器工厂知道应实例化的控制器类型为NewsController。

3、通过控制器工厂返回的Controller对象的 Execute方法,控制器通过一个实现了IActionInvoker接口的类(默认为ControllerActionInvoker类)使用 RouteData.Values[“action”]值,确定具体运行控制器中的那一个方法。

4、执行控制器中的方法生成ActionResult

5、执行ActionResult.ExecuteActionResult生成最终应答内容。

在4、5步骤中涉及到我们上一章中介绍的过滤器,而方法选择器是在第3步骤中使用的,ControllerActionInvoker类中使用ActionMethodSelector类来获取与路由信息匹配的方法,具体执行过程如下图所示:

SelectorActivity

图中红色终点表示异常。ActionMethodSelector首先从控制器中非静态方法中获取方法名称等于 RouteData.Values[“action”]或通过ActionName特性指定的名称等于 RouteData.Values[“action”]的方法列表,其次依次调用方法列表中的每个方法上的选择器,去掉选择器返回为false的部分,如 果最终由一个方法匹配则使用这个方法,如果没有方法匹配,则再检查方法列表中没有选择器的方法,如果存在一个,选中它,如果没有,则直接调用控制器的 HandleUnknownAction方法,Controller中此方法默认返回一个404的HTTP错误。

下面我们来看MVC中实现的默认选择器类型:

SelectorClass

ActionNameAttribute用于声明方法的别名,通常情况下使用ActionName将控制器中不同的方法映射为相同的控制器方法(相同的 Url访问不同的方法)。它从抽象类ActionNameSelectorAttribute继承,你也可以从此类中继承实现自己的ActionName 特性(不过似乎用处不大)。

ActionMethodSelectorAttribute抽象类是一些列选择器的基类,ActionMethodSelector选择方法时,会调用 IsValidForRequest方法来检查当前方法是否有效。MVC实现了几个默认的选择器:HttpGet、HttpPost、 HttpDelete、HttpPut以及AcceptVerbs用于检查当前请求的方式(匹配GET方法、POST方法等,AcceptVerbs用于 匹配多个方法),事实上HttpGet等选择器是对AcceptVerbs的封装。另外一个选择器:NonAction表示当前方法不对外部请求公开(它 的IsValidForRequest始终返回false)。如果我们需要实现自己的选择逻辑,则应从 ActionMethodSelectorAttribute类继承。

以下示例实现一个选择器,将根据浏览器类型将相同的Url映射到不同的控制器方法:

1、创建一个空的MVC项目

2、实现BrowseSelectorAttribute

显示行号 复制代码 BrowseSelectorAttribute
  1. public class BrowseSelectorAttribute : ActionMethodSelectorAttribute
    
  2. {
    
  3.     private string _userAgent = String.Empty;
    
  4.     public BrowseSelectorAttribute(string userAgent)
    
  5.     {
    
  6.         _userAgent = userAgent;
    
  7.     }
    
  8.     public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    
  9.     {
    
  10.         return controllerContext.HttpContext.Request.UserAgent.Contains(_userAgent);
    
  11.     }
    
  12. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }3、创建HomeController控制器

显示行号 复制代码 HomeController
  1. public class HomeController : Controller
    
  2. {
    
  3.     [ActionName("Index")]
    
  4.     [BrowseSelector("MSIE")]
    
  5.     public ActionResult IEIndex()
    
  6.     {
    
  7.         return Content("通过IE浏览器访问");
    
  8.     }
    
  9.     [ActionName("Index")]
    
  10.     [BrowseSelector("Chrome")]
    
  11.     public ActionResult ChromeIndex()
    
  12.     {
    
  13.         return Content("通过Chrome浏览器访问");
    
  14.     }
    
  15.     [ActionName("Index")]
    
  16.     public ActionResult OtherIndex()
    
  17.     {
    
  18.         return Content("通过其他浏览器访问");
    
  19.     }
    
  20. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }

最后,选择器的功能似乎与授权过滤器类似:都可以”过滤”掉控制器方法,但是他们本质上是不同的:执行授权过滤器时,MVC已经确定调用哪个控制器方法, 而选择器是在MVC选择控制器方法时得一组过滤条件。如果通过选择器过滤后没有匹配方法,默认下会返回404错误,如果通过授权过滤器终止执行某个方法, 则返回的是你在通过授权过滤器上下文参数中指定的Result。

源代码下载

[转载]android 游戏导引(1. 建立 OpenGL 项目)

mikel阅读(1051)

[转载]android 游戏导引(1. 建立 OpenGL 项目) – 圣斗士 – 博客园.

Android 游戏导引(1. 建立 OpenGL 项目)

1 搭建环境

我的环境是 mac, 鉴于 windows 下搭建的教程网上很多,大家可以 google 之。说一下 mac 下的环境搭建过程。

  1. 下载 mac 版本的 Android SDK。天朝墙很坚固,贴下 2.3 下载地址,可以用超强盗链功能的迅雷下载。
    Platform Package Size MD5 Checksum
    Mac OS X (intel) android-sdk_r08-mac_86.zip 28797617 bytes d2e392c4e4680cbf2dfd6dbf82b662c7
    Linux (i386) android-sdk_r08-linux_86.tgz 26817291 bytes 3b626645b223d137d27beefbda0c94bc
  2. 解压文件。点击 tool 目录下的 android 文件。选择要下载的相应版本的 SDK, 文档,工具等。
  3. android 要求 jdk 版本大于 1.5, mac os x上预装的是 1.6 的版本。(java -version查看)。所以不必再下 jdk 了(想下官网也没提供mac版本)。
  4. 下载 Eclipse, 版本是 for Java Developers 的就可以。解压到某个文件夹下,直接运行其中的eclipse 就使用。
  5. 安装 adt。 Eclipse 的 Help菜单下的 Install new Software…. 点击 add 按钮, 在弹出的对话框 Location 中,输入 http://dl-ssl.google.com/android/eclipse/, OK, 省略。。。
  6. 配置 Eclipse 的android 环境。重启 Eclipse, 点击Eclipse 菜单下的 Preferences, 打开设置界面。找到android 项, 设置好 android sdk 目录,点 apply, 会显示你所有的 sdk 版本。OK。
  7. 建立一个 avd。 打开 Eclipse 的 Windows 菜单下的 “Android SDK and AVD”,创建好一个 avd。就正式配置成功了。

2 创建一个 android 项目

“File” -> “New”-> “Project”: 选择 android 工程:

设置好项目的信息和sdk版本:

好了一个项目就创建完了。

3 建立 OpenGL 程序框架

默认的,建立的程序中,一个 activity 的源码如下:

public class GlGame extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

OpenGL 有自己的Surface: GLSurfaceView, 它内部提供了一个 Renderer 接口,我们所要做的就是注册一个自己的 Renderer。好了,先看我们的 activity:

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
public class GlGame extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
Renderer render = new MyRenderer();//要实现的 Renderer
GLSurfaceView glView = new GLSurfaceView(this);
glView.setRenderer(render);
setContentView(glView);
}
}

Renderer 必须要实现3个抽象方法:

  • public void onDrawFrame(GL10 gl) 绘制
  • public void onSurfaceChanged(GL10 gl, int width, int height) 窗口大小改变
  • public void onSurfaceCreated(GL10 gl, EGLConfig config) 窗口建立

添加类 MyRenderer:

01 import javax.microedition.khronos.egl.EGLConfig;
02 import javax.microedition.khronos.opengles.GL10;
03
04 import android.opengl.GLSurfaceView.Renderer;
05
06 public class MyRenderer implements Renderer{
07
08 public void onDrawFrame(GL10 gl) {
09 gl.glClearColor(1, 0, 0, 0);
10 // 清除屏幕和深度缓存
11 gl.glClear(GL10.GL_COLOR_BUFFER_BIT) | GL10.GL_DEPTH_BUFFER_BIT);
12 }
13
14 public void onSurfaceChanged(GL10 gl, int width, int height) {
15 // 设置 OpenGL 场景的大小
16 gl.glViewport(0, 0, width, height);
17 }
18
19 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
20 // TODO Auto-generated method stub
21
22 }
23
24 }

好了一个 OpenGL 框架就搭好了。接下来我们就进行可爱地屏幕绘制相关地东西了。下次再说吧。

[转载]VS2010设置断点无效的解决方法

mikel阅读(1206)

[转载]VS2010设置断点无效的解决方法 – giser2007 – 博客园.

当前不会命中断点,还没有为该文档加载任何符号”这个提示可能很多人都遇到过,首先我列几条网上常见的解决方案。

1、生成方式是Realse不是Debug

2、调试时到“附加到进程”菜单里看有没有“aspnet_wp.exe”的进程,如果没有,添加。

3、控件的事件丢失。

4、右键解决方案的属性里,看生成项里的“生成调试信息”那里是不是FALSE,如果是,改成TRUE。

5、想要调试的代码行根本不能调试。

可是我不属于其中任何一种。。但以上总结的5种的确也是原因,暂且写上。

后来突然灵光乍现,会不会是浏览器的问题,很多朋友可能默认浏览器不是IE,以前用VS2005时,都是会用IE打开页面,而到了VS2010,会 用电脑设置的默认浏览器打开,当然你把电脑的默认浏览器换成IE就可以解决,不过这样总感觉有点不爽,不过你不用担心,有解决方案。我现在是用 silverlight做开发,所以就以silverlight为例。

右键点击sampleTestPage.aspx(sample是解决方案名称)——浏览方式——把IE设置为默认——OK

[转载]android 游戏导引(2. 游戏的基础设置)

mikel阅读(1026)

[转载]android 游戏导引(2. 游戏的基础设置) – 圣斗士 – 博客园.

Android 游戏导引(2. 游戏的基础设置)

上一节已经学习了一个基本的 OpenGL 框架了,今天这一节就进一步设置一下 2D 游戏相关的东西了。对 2D 游戏的喜爱甚于 3D。 相信大多数人也是吧。

1 游戏全屏显示

2行代码搞定,一个让应用去掉自己的标题栏,一个设置全屏,放在应用的创建函数 OnCreate 中:

public class GlGame extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,
WindowManager.LayoutParams. FLAG_FULLSCREEN);
// ... 其他略
}
}

2 设置 OpenGL 的 2D 环境

我们先要设计好要建立的世界定位方式,就是坐标系,一般的 opengl 程序是标准的笛卡尔坐标系,即左下角为原点,x横向右延伸,y纵向上延伸。像著名的 cocos2d 引擎就是此种方式。我比较偏好另一套就是左上角为原点的描述方式,所以采用了此套坐标系:

GLSurfaceView 的 Renderer,给出了三个接口:创建时,大小改变时,绘制。我们在三个函数中采取的操作如下:

  • 创建时, onSurfaceCreated, 进行 OpenGL 的基本设置,如阴影平滑,深度测试啊神马的。
  • 大小改变时,onSurfaceChanged, 进行投影,视口等设置。
  • 绘制,onDrawFrame, 我们后续的游戏绘制主要在这个函数了。

2.1 onSurfaceCreated

不多说,都是基本设置。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 告诉系统对透视进行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
// 背景黑色
gl.glClearColor(0, 0, 0, 1);
// 启用阴影平滑
gl.glShadeModel(GL10.GL_SMOOTH);
// 设置深度缓存
gl.glClearDepthf(1.0f);
// 启用深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
// 所做深度测试的类型
gl.glDepthFunc(GL10.GL_LEQUAL);
}

2.2 onSurfaceChanged

public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置 OpenGL 场景的大小
gl.glViewport(0, 0, width, height);
// 设置投影矩阵
gl.glMatrixMode(GL10.GL_PROJECTION);
// 重置投影矩阵
gl.glLoadIdentity();
// 设置视口的大小
// gl.glOrthof(0, width, height, 0, -1000, 1000);
GLU.gluOrtho2D(gl, 0, width, height, 0);
// 重置模型矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}

代码中先用 glViewPort 设定了场景的大小,我们所有的opengl世界都被投射到这个场景中,在这里,我们将场景的大小设置为窗口 Surface 的大小。

继而设置了投影矩阵,投影矩阵定义了 opengl 世界怎样反应在你的场景中,在 OpenGL 中又两种投影方式: 透视和正交。透视比较接近我们现实的方式了,你的眼睛发出的光形成一个夹角,离你的眼睛越近,东西越大,范围越小;反之离眼睛越远,东西越小,视野越开 阔。因此多用于 3D 中。 而正交却是世界中的物体按照平行的光线投射到一张纸上(你的画布),仿佛被压缩在上面,无论这个物体在世界中多远,投射结果还是原来的大小, 2d 游戏多用此种投影。

设置透视投影有 glFrustum 和 glu 库的 gluPerspective, 都可以设置上面透视投影中的角度,远近等参数。还可以 gluLookAt 来设置眼睛(相机)的位置。

设置正交投影有 glOrtho 和 glu 库的 gluOrth2D :

glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
gluOrth2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);

glu版本的y轴参数不用我们设置了,因为不会用到。由于我们的坐标系以左上角为原点,所以 left 和 top 参数为 0。

glMatrixMode() 函数指定了其下面的代码操作的是何种矩阵。 我们这里用到两类矩阵变换:投影变换和模型视图变换。投影变换中我们更改了世界空间的裁切范围,在模型视图变换中,我们可以移动和旋转世界中的物体,所以 我们在绘制的时候就是在模型视图矩阵变换中。 glLoadIdentity() 用于重置矩阵,清除上次的残留信息。

代码最后设定为模型视图变换,便于我们在绘制函数中移动和变换物体模型了。

2.3 onDrawFrame

先说明一下 GLSurfaceView 的 Renderer 绘制是在独立的线程中完成的,这个函数被不断的循环调用。

前面已经设置为模型视图矩阵了,每次 onDrawFrame 的时候,我们都要 glLoadIdentity 重置一次,清除上次绘制造成的残留信息。

public void onDrawFrame(GL10 gl) {
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置模型矩阵
gl.glLoadIdentity();
//... 要添加的具体绘制代码
}

3 三角形-测试

好了,上面已经设置好了,我们先来个简单的测试吧,在你的窗口中绘制一个红色三角形。先定义好三角形的三个角度,左上角为 (60,200), 右上角为 (180, 200), 下角为 (120,300)。

01 private FloatBuffer triggerBuffer = FloatBuffer.wrap(new float[]{
02 60,200, // 左上角
03 180, 200, // 右上角
04 120,300, // 下顶角
05 });
06
07 public void onDrawFrame(GL10 gl) {
08 // 清除屏幕和深度缓存
09 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
10 // 重置模型矩阵
11 gl.glLoadIdentity();
12
13
14 gl.glPushMatrix();
15 gl.glColor4f(1, 0, 0, 0);
16 // 允许设置顶点
17 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
18 gl.glVertexPointer(2, GL10.GL_FLOAT, 0, triggerBuffer);
19 gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
20 // 取消设置顶点
21 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
22 gl.glPopMatrix();
23 }

主要代码包围在了 glPushMatrix() 和 glPopMatrix() 中,OpenGL内部维护了各种矩阵栈,栈的操作就是 push 和 pop 了。这里我们的操作是在模型视图矩阵变换中进行的,所以操作的是模型视图栈。之所以压栈,我们不想绘制三角形的矩阵信息污染到外层的矩阵中,比如颜色设置 等。所以大家在绘制一个单位体的时候尽量使用 push 和 pop 操作。

先用 glColor4f 设置绘制颜色为红色。opengles 中少了 glBegin, glEnd 类标准函数,采用了数组顶点来设坐标方式,主要还是性能考虑吧。默认是关掉这个选项的,所以先开启,不用了再关闭: glEnableClientState(GL10.GLVERTEXARRAY), glDisableClientState(GL10.GLVERTEXARRAY).

指定数组顶点用 glVertexPointer, 指定完了就可以用 glDrawArray() 来将指定的数组中的顶点绘制出来了。这里有必要罗嗦一下这两个函数了,因为这两个函数用到的太多了。先看第一个:

void glVertexPointer (int size, int type, int stride, Buffer pointer)

参数:

size
每个顶点有几个数值描述。必须取值2,3,4之一。初始值是 4.
type
数组中每个顶点坐标的类型。取值:GLBYTE, GLSHORT, GLFIXED, GLFLOAT。初始值为 GLFLOAT。
stride
数组中每个顶点间的间隔,步长(字节位移)。取值若为 0 表示数组是连续的。初始值为 0。
pointer
就是你的数组了,存储着每个顶点的坐标值。初始值为 0。

注意了, type 中告诉 opengl 你的数组类型。 GLBYTE, GLSHORT, GLFLOAT 对应 byte[], short[], float[]. GLFIXED 对应 int[]. 有一个特别的地方, GLFIXED 描述的时候,大家的点坐标单位是 0x10000, 比如一个点是 (60, 120), 用 GLFIXED 的时候,需要设置为 (60 * 0x10000, 120 * 0x10000), 所以经常看到 int one = 0x10000 的编程语句, 这个数值就是这么来的。

再来看 glDrawArray:

void glDrawArrays (int mode, int first, int count);

参数:

mode
指定你要绘制何种图元, opengl 中的图元就这几个: GLPOINTS, GLLINESTRIP, GLLINELOOP, GLLINES, GLTRIANGLESTRIP, GLTRIANGLEFAN, GLTRIANGLES.
first
在已制定的数组中的开始位置(索引位置)
count
点的绘制次数, 比如我们绘制一个三角形,就是绘制三个顶点,即此参数为 3。

这两个函数就介绍这么多了,足以应付这个程序了。有问题可以查官方函数文档: http://www.khronos.org/opengles/sdk/1.1/docs/man/

好了,代码就介绍完了。一个三角形大家应该会绘制了吧,大家可以试试其他的图元,点,线,四边形,多边形等等。

学习愉快。

[转载]android 游戏导引(3. 图形引擎之模型管理)

mikel阅读(948)

[转载]android 游戏导引(3. 图形引擎之模型管理) – 圣斗士 – 博客园.

Android 游戏导引(3. 图形引擎之模型管理)

上一节中,我们构建了一个自己的场景世界。可以在内部绘制一些基本图元了。本来这一节要说说贴图的,想想还是休息下,放个小插曲,思考下模型的管 理,游戏引擎相关的东西。这些东西跟 cocos2d 很像,可能是 iphone 下常用 cocos2d 的缘故吧, 反正成熟且成功的东西,我们拿来用就行了。

源码下载: 点我吧

1 面向对象吧

基于教程到这里的进度,我们要会议几个图元模型的话,就要显式的在 onDrawFrame 中绘制。基于面向对象的思想,我们知道我们绘制的模型仅仅是一个渲染单位而已,并且我们在 onDrawFrame 的操作只是一个opengl 访问。 因此基于接口,我们的一个渲染模型类就诞生了。

2 模型父子链:树

你也许会说,上面要渲染的模型太多的时候太散乱了。想到这里,说明你还是一个合格的程序员,有着对完美的执着。与其手动的管理,不如让程序自己管 理。大家想想,如果是一个复杂的模型,比如一个人,它是有众多细小的模型构成的,比如脑袋,胳膊,腿等, 而脑袋又有鼻子,耳朵,嘴巴等构成。哈哈,这就是一个树形的结构了,看下结构特征:

  • 一个父节点有0个或多个子节点
  • 一个字节点最多有一个父节点

一个对象的父子关系如下:

现在游戏多采用树形模型管理,而且在 GUI 系统中也大放异彩。有了树形管理,我们可以轻易的将一种类型的鼻子安装到不同人的脑袋上,不用重复发明轮子了。

好了,现在我们的 onDrawFrame 中的形式是这样的了,右边的模型对象是一个封装好的单位,可能内部许多子节点。

3 层式屏幕管理

我们最终是要把模型绘制到屏幕上,我们思考下面问题:

  1. 屏幕上有众多模型需要渲染。比如一个人在草原中散步。概括讲人模型,还有草原,太阳,河流等等。
  2. 屏幕上的模型又可以归为不同的类别。如草原,太阳,河流这些是背景,我们控制的人是前景。

于是,层式管理来了,我们的渲染根结点称为场景 Scene, 分为不同的层 Layer, 在每一层上挂接着具体的模型。看下图就明白了,(注:来自cocos2d-python 文档)

为了向渲染 onDrawFrame 突出接口,Scene 和 Layer 也是一个渲染模型单位。

看看我们添加层式屏幕管理后的效果, 我们将所有要绘制的东西添加到一个场景中,就直接对这个场景节点 glVisit() 就可以了:

每个模型突出一个 glVisit(),这个函数的主要功能是调用自身绘制函数 draw() 然后对各个字节点逐个调用 glVisit().

4 游戏引擎

一个游戏引擎的设计遵循的原则是: 逻辑要和渲染分离,尽量将opengl 最小程度封装在底层,上层使用尽量不要触及底层api,尽量让核心代码原理Android系统的api。组件最小集合应该包含下面几种:

  • 图形引擎,我们上述讨论的就是
  • 事件分派系统,手机应用是基于触摸事件的,所以独立出来(事实上,也是一个调度器)
  • 时间调度器,游戏除了触摸时间还有自身的时间事件,如动画

其他的可能用到的组件有碰撞检测系统,输入系统,寻路算法等等。

游戏引擎的丰富会在以后的教程中逐步增设,今天这一节算是个开头,弄了个简单的图形引擎,也会在接下来随着需求的需要丰富其功能和接口。为了便于从 android 相关代码中解脱出来,添加一个 GameSystem 单件类:

public class GameSystem {
private static GameSystem instance_ = new GameSystem();
public static GameSystem getInstance()  { return instance_;   }
// 场景
private GlObject runningScene_= null;
private int width = 0; // opengl的场景尺寸
private int height = 0;
public void setWindowSize(int width, int height)
{
this.width = width;
this.height = height;
}
public int getWindowWidth() { return this.width;  }
public int getWindowHeight()    { return this.height; }
// 设定场景
public void setScene(GlObject scene){       runningScene_ = scene;  }
//
public void glVisit(GL10 gl)    {
if (runningScene_ != null)
runningScene_.glVisit(gl);
}
}

我们将其插入到 android 代码的三个地方:

  1. Activity 的 onCreate 方法中,设定WindowSize: setWindowSize. 设定初始场景 setScene
  2. Renderer 的 onDrawFrame 方法中,来访问GameSystem 的游戏场景 glVisit

5 代码实现

在屏幕中除了上面的接口以外,增加的是坐标点,存储的是相对坐标,相对于父节点而言,用于描述模型的位置。这个坐标点不要理解为顶点数组中的顶点坐标。 可以这样理解,你将画笔移动到坐标点处,然后在此处依据顶点数组来绘制。代码较长,省略了一些,可以下载源码。

public class GlObject {
protected GlObject parent = null; // 父节点
protected LinkedList<GlObject> children = new LinkedList<GlObject>();// 字节点
private boolean visible = true; // 可访问(对 gl)
float x = 0;
float y = 0;
// 父子链管理
public void addChild(GlObject obj)  {
children.add(obj);
obj.parent = this;
}
public void removeChild(GlObject obj)   {
obj.parent = null;
children.remove(obj);
}
public void setParent(GlObject p)   {
if (parent != null) parent.removeChild(this);
p.addChild(p);
}
public GlObject getParent() { return parent;  }
// 坐标
float getX()    { return x;   }
float getY()    { return y;   }
void setXY(float x, float y)    {
this.x = x;
this.y = y;
}
// visible
public void setVisible(boolean v)   {       visible = true; }
public boolean getVisible() { return visible; }
// 外部gl访问接口
public void glVisit(GL10 gl)    {
if (!visible) return;
gl.glPushMatrix();
gl.glTranslatef(x, y, 0);
this.draw(gl);
for (GlObject child : children)
child.glVisit(gl);
gl.glPopMatrix();
}
// 绘制自身:子类重写此方法
public void draw(GL10 gl)   {}
////////////////////////////////////////////////////////////////////////////////////////
////  一些常用的图元绘制
// 绘制正方形
public static void drawQuater(GL10 gl, float left, float top, float right,float bottom) {
// 略
}
// 绘制三角形
public static void drawTriangle(GL10 gl, float oneX, float oneY,float twoX, float twoY, float threeX, float threeY) {
// 略
}
// 绘制扇形
public static void drawArc(GL10 gl, float length, float startAngle,float sweepAngle)    {
// 略
}
// 绘制直线
public static void drawLine(GL10 gl, float oneX, float oneY, float twoX,float twoY) {
// 略
}
}

6 绘制一个 android 机器人

代码还是以前的,绘制图元,只不过经过本章的洗礼,代码封装性和扩展性上有了进步。一个机器人有下面的结构组成:

  • 身体
    • 眼睛 * 2
    • 天线
  • 腿 * 2
  • 胳膊 * 2
  1. 设定 GameSystem 的初始游戏场景:
public class GlGame extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
// 。。。。略
// 初始化游戏系统:
// ... 屏幕大小
{
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
GameSystem.getInstance().setWindowSize(dm.widthPixels, dm.heightPixels);
}
// ...设定初始场景
GameSystem.getInstance().setScene(new AndroidScene());
//..... setContentView 代码
}
}

2. 添加一个场景类 AndroidScene 作为我们的画布,我们将会在其上绘制机器人

public class AndroidScene extends GlObject{
AndroidRobot robot = new AndroidRobot();
public AndroidScene(){
super();
// 在屏幕中心绘制
robot.setXY(GameSystem.getInstance().getWindowWidth()/2, GameSystem.getInstance().getWindowHeight()/2);
addChild(robot);
}
}

3. 添加一个机器人渲染模型 AndroidRobot, 代码太长, 折叠之:

好了,本次就唠叨到这里,学习愉快。

[转载]ASP.NET MVC2.0本地化另类解决方案

mikel阅读(956)

[转载]MVC2.0本地化(另类解决方案) – RyanDing – 博客园.

本文是对MVC2.0本地化(另类解决方案)<上>这篇文章内介绍的MVC2.0本地化功能进行加强、细化的结尾篇。如果存在不足的地方,希望您指出。

如何对上篇文章进行加强以及细化呢?主要从以下三点开始。

1、根据用户浏览器自动语言判断,同时也可由用户自定义站点语言。

2、多语言加入,类似resource文件,可以有多个语言资源库。

3、全局本地化,可以本地化非页面内容,比如为台异步到前台的json数据本地化等。

主要步骤如下:

一、根据用户浏览器自动语言判断,同时也可由用户自定义站点语言


这个实现思路为:首选判断存储在客户端的用户“主动”设定的cookie。如果该值为空,则根据用户浏览器自动识别语言。如果不为空则根据用户设定好的 cookie值获取站点语言。那如何根据客户浏览器识别语言,我们要在web.config的<system.web>节点添 加<globalization enableClientBasedCulture=”true” culture=”auto” uiCulture=”auto”/>设置。根据此设置服务器端就就可以使用 System.Threading.Thread.CurrentThread.CurrentCulture识别用户浏览器的语言。贴出global中 部分代码以供参考:

01 protected void Application_BeginRequest(Object sender, EventArgs e)
02 {
03 HttpCookie lang = Request.Cookies["Lang"];
04 if (lang != null)
05 {
06 if (Response.ContentType == "text/html" || Response.ContentType == "application/json")
07 Response.Filter = new LocalizationHandler(Response.Filter, lang.Value);
08 return;
09 }
10 string langFromBrowser = System.Threading.Thread.CurrentThread.CurrentCulture.ToString();
11 string strLang = string.Empty;
12 if (string.Compare("zh-CN", langFromBrowser, true) == 0)
13 {
14 strLang = "SimplifiedChinese";
15 }
16 else if (string.Compare("zh-Hant", langFromBrowser, true) == 0)
17 {
18 strLang = "TraditionalChinese";
19 }
20 else if (langFromBrowser.Contains("en"))
21 {
22 strLang = "English";
23 }
24 else
25 {
26 strLang = "English";
27 }
28
29 if (Response.ContentType == "text/html" || Response.ContentType == "application/json")
30 Response.Filter = new LocalizationHandler(Response.Filter, strLang);
31 }

通过以上代码块,服务端就可以聪明的识别:到底是根据客户端的cookie还是根据用户浏览器语言设定来显示站点语言。

二、多语言加入,类似resource文件,可以有多个语言资源库。


这个扩展就非常简单了,在上篇文章中只有一个XML文件对应于该XML的.net4.0缓存。如果是多语言包,我们只要建立多个XML文件。以及每个XML文件相对应的缓存机制即可。截图如下:

Resources文件下的语言包分为为:英文、简体中文、繁体中文。xml格式请阅 MVC2.0本地化(另类解决方案)<上>

如何获取xml内的翻译文本,上篇文章中写的很清晰了。唯一要注意的是为每个xml文件(语言包)建立独立的缓存机制即可。

三、全局本地化,可以本地化非页面内容,比如为台异步到前台的json数据本地化等


首先看本文实现步骤的第一点中贴出代码第6行,if (Response.ContentType == "text/html" || Response.ContentType == "application/json") 这里控制了json格式的本地化。说白了以后后台的异步到前台的JSON格式的本地化资源,我们也可以写在XML文件中,只要通过Response.Filter 机制就可以本地化,而无需增加额外的本地化代码。

具体实现截图如下:

MVC2.0 JsonResult:

JQuery调用:

最终会将“<=language is applied>”根据不同的国家的用户分别显示成三种语言:

简体:语言已设定。

繁体:語言已經設定。

英文:Language is applied.

四、程序运行截图


五、小结


经过上文的介绍,基本上已完成了本地化另类解决方案。该方案也存在其缺点。但是优点是,可以动态的改变本地化内容。而不需要重新修改源代码,只要修改相应的xml文件内容即可。

希望本篇文章可以给您带来帮助,如有不足之处欢迎指出,谢谢!

[转载]actionscript3.0简单粒子效果

mikel阅读(1148)

[转载]actionscript3.0简单粒子效果 – 火星人 – 博客园.

从天地会论坛的MoonSpirit师兄那里学来了用actionscript3.0中的BitmapData粒子实现一个简单的图片演示效果。

particleVO.as:

   1:  package com.helloshp.vo
   2:  {
   3:      import flash.display.BitmapData;
   4:  
   5:      //粒子对象属性
   6:      public class ParticleVO
   7:      {
   8:          public var bmd:BitmapData;//用来装载切图
   9:          public var crtPosX:Number;//该粒子对象当前出现在舞台的X位置
  10:          public var crtPosY:Number;//该粒子对象当前出现在舞台的Y位置
  11:          public var tragetX:Number;//该粒子最后要运动到的舞台的X位置
  12:          public var tragetY:Number;//该粒子最后要运动到的舞台的Y位置
  13:      }
  14:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas,”Courier New”,courier,monospace; background-color: rgb(255, 255, 255); }.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }

Main.as:

   1:  package
   2:  {
   3:      import com.helloshp.vo.ParticleVO;
   4:  
   5:      import flash.display.Bitmap;
   6:      import flash.display.BitmapData;
   7:      import flash.display.Sprite;
   8:      import flash.events.Event;
   9:      import flash.events.MouseEvent;
  10:      import flash.events.TimerEvent;
  11:      import flash.geom.Point;
  12:      import flash.geom.Rectangle;
  13:      import flash.utils.Timer;
  14:  
  15:      [SWF(width="800",height="600")]
  16:      public class Main extends Sprite
  17:      {
  18:          [Embed(source="assets/imgTest0.jpg")]
  19:          private var imgTest0:Class;
  20:          [Embed(source="assets/imgTest1.jpg")]
  21:          private var imgTest1:Class;
  22:          [Embed(source="assets/imgTest2.jpg")]
  23:          private var imgTest2:Class;
  24:          [Embed(source="assets/imgTest3.jpg")]
  25:          private var imgTest3:Class;
  26:          [Embed(source="assets/imgTest4.jpg")]
  27:          private var imgTest4:Class;
  28:  
  29:          private var bmpimgTest:Bitmap;
  30:          private var bmp:Bitmap;
  31:          private var bmd:BitmapData;
  32:          private var particleVOList:Array;
  33:          private var currentIndex:int;
  34:          private var isStop:Boolean;
  35:          private var timer:Timer;
  36:  
  37:          public function Main()
  38:          {
  39:              init();
  40:          }
  41:  
  42:          private function init():void{
  43:              initData();
  44:              addEventListener(Event.ENTER_FRAME,efHandler);
  45:              stage.addEventListener(MouseEvent.CLICK,clickHandler);
  46:              timer = new Timer(3500);
  47:              timer.addEventListener(TimerEvent.TIMER,timerHandler);
  48:              timer.start();
  49:          }
  50:  
  51:          private function initData():void{
  52:              //清除舞台所以的可视对象
  53:              for(;this.numChildren>0;){
  54:                  this.removeChildAt(0);
  55:              }
  56:  
  57:              //实例化用于创建粒子对象的数组
  58:              particleVOList = new Array();
  59:              //把即将要切片的imgTest.jpg图片装载到一个Bitmap视图容器里面
  60:              bmpimgTest = getImg( currentIndex );
  61:              //实例化待加载切图的画布
  62:              bmd = new BitmapData(800,600,true,0);
  63:              //把画布加载到显示容器里面去,等哈儿显示所有的粒子对象用
  64:              bmp = new Bitmap( bmd );
  65:              //把显示容器加载到舞台
  66:              addChild(bmp);
  67:              //初始化粒子对象
  68:              initParticleVO();
  69:          }
  70:  
  71:          private function getImg(crtIndex:int):Bitmap{
  72:              switch(crtIndex){
  73:                  case 0:
  74:                      return new imgTest0();
  75:                      break;
  76:                  case 1:
  77:                      return new imgTest1();
  78:                      break;
  79:                  case 2:
  80:                      return new imgTest2();
  81:                      break;
  82:                  case 3:
  83:                      return new imgTest3();
  84:                      break;
  85:                  case 4:
  86:                      return new imgTest4();
  87:                      break;
  88:                  default:
  89:                      return new imgTest0();
  90:                      break;
  91:              }
  92:  
  93:          }
  94:  
  95:          //当鼠标点击后,暂停或者继续播放
  96:          private function clickHandler(e:MouseEvent):void{
  97:              isStop = isStop?false:true;
  98:              if(isStop){
  99:                  timer.stop();
 100:              }
 101:              else{
 102:                  timer.reset();
 103:                  timer.start();
 104:              }
 105:  
 106:          }
 107:  
 108:          //初始化所有的粒子对象,每个粒子对象其实就是把加载的testImg图片,通过2重循环切成一个一个的bitmapData,
 109:          //并设置每个粒子对象的当前坐标(待会儿舞台显示出现的位置),目标坐标(就是粒子对象要移动到的位置),并存放到数组。
 110:          //通过2重循环切testImg图的时候关键是,新建立一个尺寸10 X 10 大小的bitmapData对象,然后通过它的copyPixels()方法去切加载的图片。
 111:          //copyPixels(),的参数1: 就是要切的图片对象的bitmapData,参数2: 创建一个要切的那一个方块对象,该方块对象的x,y坐标就是该方块最终要放置到舞台上的坐标
 112:          //参数3:切图的坐标点,默认设置为原点。        
 113:          private function initParticleVO():void{
 114:              //行数为50行,因为每次切图的高就是10像素,60*10=600,刚好就是我的图片的高            
 115:              for( var row:int=0;row<60;row++ ){
 116:                  //宽度为80行,因为每次切图的宽就是10像素,80*10=800,刚好就是我的图片的宽
 117:                  for(var col:int=0;col<80;col++){
 118:                      //初始化每一个粒子VO
 119:                      var pvo:ParticleVO = new ParticleVO();
 120:                      pvo.tragetX = col * 10;//粒子目标X坐标(就是粒子对象最终要移动到的X位置)
 121:                      pvo.tragetY = row * 10;//粒子目标Y坐标(就是粒子对象最终要移动到的Y位置)                    
 122:                      pvo.crtPosX = col * 10 + Math.random() * 1000; //粒子在舞台显示的X位置.通过随机数打乱初始位置
 123:                      pvo.crtPosY = row * 10 + Math.random() * 1000;//粒子在舞台显示的Y位置,通过随机数打乱初始位置
 124:                      pvo.bmd = new BitmapData(10,10,true,0);//创建一个空白的画布
 125:                      //关键代码,就是切图操作
 126:                      pvo.bmd.copyPixels( bmpimgTest.bitmapData,new Rectangle(pvo.tragetX,pvo.tragetY,10,10),new Point() );
 127:                      //把每个切出来的图,装载到一个数组里面(等哈儿再渲染到舞台上出来)
 128:                      particleVOList.push( pvo );
 129:                  }
 130:              }
 131:          }
 132:  
 133:          private function efHandler(e:Event):void{
 134:              if(isStop) return;
 135:              //bmd.lock()
 136:              //创建一个800 X 600 的空白的画布
 137:              bmd = new BitmapData(800,600,true,0);
 138:              //循环取出每一个刚刚切好的图片,然后再转载到画布里面
 139:              for(var i:int=0;i<particleVOList.length;i++){
 140:                  var pvo:ParticleVO = particleVOList[i];
 141:                  pvo.crtPosX += ( pvo.tragetX - pvo.crtPosX ) / ( 2 + Math.random() * 20 );//X轴缓动效果
 142:                  pvo.crtPosY += ( pvo.tragetY - pvo.crtPosY ) / ( 2 + Math.random() * 20) ;//Y轴缓动效果
 143:                  //关键代码,把数组里面装的每一个小的切图,装载到800 X 600 的大的画布里面去。装载的时候,都是按照粒子对象对应的X,Y坐标装载的。
 144:                  bmd.copyPixels( pvo.bmd, new Rectangle(0,0,10,10 ),new Point(pvo.crtPosX,pvo.crtPosY) );
 145:              }
 146:              //把装载好的画布,赋值给显示对象,让可视对象bmp在舞台上渲染出图片出来
 147:              bmp.bitmapData = bmd;
 148:              //bmd.unlock();
 149:          }
 150:  
 151:          private function timerHandler(e:TimerEvent):void{
 152:              currentIndex++;
 153:              if(currentIndex > 4 ){
 154:                  currentIndex = 0;
 155:              }
 156:              initData();
 157:          }
 158:      }
 159:  }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas,”Courier New”,courier,monospace; background-color: rgb(255, 255, 255); }.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }代码下载:http://files.cnblogs.com/bigbigdotnet/particle.rar

[转载]WPF下可编辑Header的Tab控件实现

mikel阅读(948)

[转载]WPF下可编辑Header的Tab控件实现 – 葡萄城控件技术团队博客 – 博客园.

介绍

有这样一个需求,当用户双击Tab控件Header区域时, 希望可以直接编辑。对于WPF控件,提供一个ControlTemplate在加上一些Trigger就可以实现。效果如下:

代码

首先,我们需要给Tab Header设计一个ControlTemplate。类似一个TextBlock,双击进入编辑状态。 所以Xaml如下:

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:EditableTabHeaderControl}">
<Grid>
<TextBox x:Name="PART_TabHeader" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}" Visibility="Collapsed"/>
<TextBlock x:Name="PART_TextBlock" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsInEditMode" Value="True">
<Trigger.Setters>
<Setter TargetName="PART_TabHeader" Property="Visibility" Value="Visible"/>
<Setter TargetName="PART_TextBlock" Property="Visibility" Value="Collapsed"/>
</Trigger.Setters>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

接下来,我们需要定义个“EditableTabHeaderControl”类,它具有控制TextBox和TextBlock的能力。如下:

namespace EditableTabHeaderDemo
{
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
/// <summary>
/// Header Editable TabItem
/// </summary>
[TemplatePart(Name = "PART_TabHeader", Type = typeof(TextBox))]
public class EditableTabHeaderControl : ContentControl
{
/// <summary>
/// Dependency property to bind EditMode with XAML Trigger
/// </summary>
private static readonly DependencyProperty IsInEditModeProperty = DependencyProperty.Register("IsInEditMode", typeof(bool), typeof(EditableTabHeaderControl));
private TextBox textBox;
private string oldText;
private DispatcherTimer timer;
private delegate void FocusTextBox();
/// <summary>
/// Gets or sets a value indicating whether this instance is in edit mode.
/// </summary>
public bool IsInEditMode
{
get
{
return (bool)this.GetValue(IsInEditModeProperty);
}
set
{
if (string.IsNullOrEmpty(this.textBox.Text))
{
this.textBox.Text = this.oldText;
}
this.oldText = this.textBox.Text;
this.SetValue(IsInEditModeProperty, value);
}
}
/// <summary>
/// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.textBox = this.Template.FindName("PART_TabHeader", this) as TextBox;
if (this.textBox != null)
{
this.timer = new DispatcherTimer();
this.timer.Tick += TimerTick;
this.timer.Interval = TimeSpan.FromMilliseconds(1);
this.LostFocus += TextBoxLostFocus;
this.textBox.KeyDown += TextBoxKeyDown;
this.MouseDoubleClick += EditableTabHeaderControlMouseDoubleClick;
}
}
/// <summary>
/// Sets the IsInEdit mode.
/// </summary>
/// <param name="value">if set to <c>true</c> [value].</param>
public void SetEditMode(bool value)
{
this.IsInEditMode = value;
this.timer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
this.timer.Stop();
this.MoveTextBoxInFocus();
}
private void MoveTextBoxInFocus()
{
if (this.textBox.CheckAccess())
{
if (!string.IsNullOrEmpty(this.textBox.Text))
{
this.textBox.CaretIndex = 0;
this.textBox.Focus();
}
}
else
{
this.textBox.Dispatcher.BeginInvoke(DispatcherPriority.Render, new FocusTextBox(this.MoveTextBoxInFocus));
}
}
private void TextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.textBox.Text = oldText;
this.IsInEditMode = false;
}
else if (e.Key == Key.Enter)
{
this.IsInEditMode = false;
}
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
this.IsInEditMode = false;
}
private void EditableTabHeaderControlMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
this.SetEditMode(true);
}
}
}
}

这里有一个问题,当控件进入编辑状态,TextBox变为可见状态时,它不能自动获得focus。一种解决办法是挂一个Timer,每1毫秒轮询一次,检查状态并控制focus。

现在就来添加一个WPF TabControl,并应用ItemContainerStyle。然后双击Header,可以编辑啦~

<Window x:Class="EditableTabHeaderDemo.MainWindow"
xmlns:local="clr-namespace:EditableTabHeaderDemo"
Title="EditableTabHeaderDemo" Height="300" Width="500">
<Window.Resources>
<Style x:Key="EditableTabHeaderControl" TargetType="{x:Type local:EditableTabHeaderControl}">
<!-- The template specified earlier will come here !-->
</Style>
<Style x:Key="ItemContainerStyle" TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<local:EditableTabHeaderControl
Style="{StaticResource EditableTabHeaderControl}">
<local:EditableTabHeaderControl.Content>
<Binding Path="Name" Mode="TwoWay"/>
</local:EditableTabHeaderControl.Content>
</local:EditableTabHeaderControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="ContentTemplate">
<Grid>
<TextBlock HorizontalAlignment="Left" Text="{Binding Name}"/>
<TextBlock HorizontalAlignment="Center" Text="{Binding City}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Grid.Row="0" ItemsSource="{Binding Data}" ItemContainerStyle="{StaticResource ItemContainerStyle}" ContentTemplate="{StaticResource ContentTemplate}" />
</Grid>
</Window>

许可证

本文以及示例代码文件遵循The Code Project Open License(CPOL)

源码下载

EditableTabHeaderSolution.zip

英文链接:Header Editable Tab Control in Wpf