[转载]推荐25个提高网站可用性和转化率的工具

mikel阅读(1999)

[转载]推荐25个提高网站可用性和转化率的工具 – 梦想天空 – 博客园.

网站建设的核心关键是“如何发现潜在客户并有效的转化为合作客户”,这里提到有效的转化也就是说找到的客户不一定会选用我们的产品或者服务,成 为了我们的客户的转化才是有效的转化,那么在竞争如此激烈的网络时代,我们该怎么去挖掘潜在客户并提高我们网站的转化率呢?关键之一就是提高网站的可用 性。

对于网站来说,可用性是指用户能否有效地找到所需的信息或完成他的任务,效率如何以及是否让人有愉快满意的感受。如果网站可用性较差,会浪费用 户的时间,大大降低网站的回头访问率,这对网站的生存是一个至关重要的问题。因此,为网站开发人员提供可用性改善建议和改进工具是十分必要的。

usability testing 25 Tools to Improve Your Websites Usability

如何知道访客是否喜欢你的网站?借助可用性检测工具追踪用户行为可以回答这个问题。跟踪到的数据可以给你提供精确的信息,让你知道用户喜欢你网 站的什么地方,哪些区域是最突出,哪些地方是用户最容易忽略的。不要小看这些收集到的数据,因为这些数据可以帮助你有效的提高网站的点击率和转化率。

改善您的网站不要挑时间,当你发现你的网站吸引不了多少访客的时候,就应该立即行动了。 这里收集了很多非常好的工具,通过不同的方法追踪用户的行为并提供给你改进网站可用性的建议。它们中有些可能不是免费的,但一个好的回报是值得去付出的。 为大家猎取所需的工具是一件开心又很有意义的事情!

Userfly

Userfly可以提供免费的网页访客动作记录服务。只需要在网页中添加一段简单的JavaScript代码,就可以记录访客从打开该网页到关闭整 个过程中的动作。Userfly能够记录的内容包括鼠标的移动、点击以及键盘输入等动作。对于网站拥有者来说,Userfly可以很方便的对用户行为进行 检测和分析,通过A/B Testing等方法为网站UI/UE提供非常有价值的信息。

userfly 25 Tools to Improve Your Websites Usability

Attention Wizard

Attention Wizard是一个视觉工具,它可以帮助您提高转换率,轻松地识别目标网页存在的问题。 Attention Wizard用高级人工智能算法来模拟人类视线的轨迹和聚焦点。Attention Wizard能够立刻对你提交的网页样本进行处理并生成“眼球轨迹热力图”。这个“热力图”可以预测你网站的访问者在最初的几秒内是如何浏览的。 生成的结果中,有75%都是通过眼睛跟踪和鼠标跟踪来完成的。Attention Wizard有简易版、高级版和终极版三个版本。

attentionwizard 25 Tools to Improve Your Websites Usability

ClickDensity

一个完整的可用性工具包,通过从访客那里获取的实时数据来改善网站内容结构,优化链接和广告的投放位置,分析和提高网站的粘性。 它将告诉你访客究竟在何处点击。

注册之后,把网站提供的一段JavaScript代码加入到你的网站中即可,当用户访问网站的时候,一个小的附加文件会自动下载用于传输用户的点击事件。该产品提供免费试用版,可付费升级到不同的高级版本。

clickdensity 25 Tools to Improve Your Websites Usability

4Q Online Survey

用户是否对网站真正的满意? 第四季的网上调查,一个免费的解决方案,可以告诉你关于你网站的访问者的详细信息,例如用户为什么访问你的网站,是否以正确的操作方式完成任务,如果不是,会是原因是什么阻碍了他们。 这项调查可以在用户中收集到大量的反馈。

4qsurvey 25 Tools to Improve Your Websites Usability

ChalkMark

Chalkmark可以让你快速创建一个项目,在访客中对界面原型进行调查以了解用户关注的焦点在哪,结果中可以查看每项任务的热图,并知道有多少用户跳过了任务。可以免费注册,也可以按月或者年付费到不限制任务数级别。

chalkmark 25 Tools to Improve Your Websites Usability

ClickHeat

是GPL许可下的开源软件,是一个很强大的JavaScript 库,它可以帮你统计一个页面上用户点击的热度分布图,支持中文显示。

clickheat 25 Tools to Improve Your Websites Usability

Clicktale

它是对你的网站访客浏览行为进行分析的一个工具,以类似视频的方式将访问者在你的网站上进行的操作全部记录下来,你可以在线观看也可以下载到电脑 上。利用ClickTale的访客行为视频记录,可以帮你更好的布局你的网页,给访问者带来更好的用户体验进而提升转化率。 它还提供了实时监控的性能分析,转化分析,链接分析,先进的过滤和市场分析。

clicktale 25 Tools to Improve Your Websites Usability

Clixpy

Clixpy几乎记录了访客的所有动作,如鼠标移动,点击,滚动和表单输入。 这些动作对于定位用户发现容易或者困难内容,那些东西最吸引他们以及为什么用户会离开网站有很大的帮助。这有助于优化目标网页,并帮助您把访问者转化为客户。

clixpy 25 Tools to Improve Your Websites Usability

Concept Feedback

Concept Feedback通过在线的专业团队快速的对网站设计,可用性和营销等方面提供反馈。对于公开的意见,团队中WEB方面的专业人员会给你一个整体的意见, 对于私密反馈,你可以邀请特定专家提供反馈,并组织出图形化的结果。 专业的反馈来自于在设计,可用性和营销领域的专家。

conceptfeedback 25 Tools to Improve Your Websites Usability

CrazyEgg

CrazyEgg能够监控一个页面的所有点击并能清楚的显示页面的点击热图,可以相当准确地监控所有页面的点击位置。

crazyegg 25 Tools to Improve Your Websites Usability

FeedbackArmy

你可以通过提交你网站的问题来获得一个快速的可用性测试并得到10项反馈。不过,不要支付15美元,如果想得到50项反馈则需要55美元。

feedbackarmy 25 Tools to Improve Your Websites Usability

Feng GUI

Feng GUI通过模拟人的视觉在开始的5秒内看到的东西来生成热点图,它为设计和开发人员提供了测试服务,通过分析观察角度、品牌效益和预测效果。

feng gui 25 Tools to Improve Your Websites Usability

Five Second Test

通过汇集用户在5秒内回答的问题集来改善目标网页以让访客关注关键内容。

fivesecondtest 25 Tools to Improve Your Websites Usability

Google Site Search

在Google Site Search中,强化了企业网站的索引范围;一个托管的搜索解决方案,可以呈现无广告的搜索结果,提高网站的转化率和销售额,降低成本。

sitesearch 25 Tools to Improve Your Websites Usability

Kampyle

Kampyle可以让你在你的网站出现一个在线发送反馈信息的按钮,点击此按钮可以弹出调查表,让访客可以反馈信息。Kampyle提供了反馈表单模板,你只需要简单修改即可,另外Kampyle还提供了丰富的反馈统计功能。

kampyle 25 Tools to Improve Your Websites Usability

Loop 11

使用Loop 11可以很容易的创建一个专业的可用性测试,收集反馈并及时的得到可用性的量化数据,然后立即看到结果。 支持超过40种语言。

loop11 25 Tools to Improve Your Websites Usability

OpenHallway

OpenHallway记录用户在远程或本地的可用性数据。用户的屏幕和语音会被记录并被上传到您的帐户。

openhallway 25 Tools to Improve Your Websites Usability

Silverback

一个用于设计人员和开发人员通过捕获屏幕活动来进行可用性测试的软件,可以记录测试者的表情和声音,有批量导出功能并可以标示出任何值得注意的时刻。

silverbackapp 25 Tools to Improve Your Websites Usability

Site Tuners

它通过指导你了解在目标网页中存在的转化率问题并回答您的具体的转换相关的问题。通过优化目标网页快速而简便的获得最好的转换率。

sitetuners 25 Tools to Improve Your Websites Usability

TechSmith

TechSmith是世界领先的为个人和专业领域提供屏幕捕获和录制软件的供应商。人们普遍使用TechSmith产品来从他们的计算机屏幕上捕获 内容。Morae是用于了解你的客户的一个很好的软件。使用Morae一个好处是,它会自动计算并给出效果和满意度,而不只是分析和报告。

morae 25 Tools to Improve Your Websites Usability

Usabilla

Usabilla帮助网站所有者更深入的了解他们的客户,不仅仅是数据。收集有关网页,实物模型,线框或任何图像的视觉反馈。

usabilla 25 Tools to Improve Your Websites Usability

User Testing

UserTesting观察用户在自己的环境中的活动,倾听他们的想法,然后形成结论。

usertesting 25 Tools to Improve Your Websites Usability

Webnographer

是一个远程的可用性测试工具,测试网站、Web应用程序或者原型在自然环境中可操作行的指标,通过调查问卷捕获用户的思想。

webnographer 25 Tools to Improve Your Websites Usability

Webtrends

优化重点在于发现市场潜力和精准的投递内容。世界上数以千计的网站智能企业,包括半数以上的财富和全球500强企业,正依靠WebTrends提高 他们的网站转换率、优化他们的市场营销效果,以获得更高的投资回报。作为全球网站分析行业的领导者,WebTrends凭借其多次获得业界大奖、全面地咨 询服务和无与伦比的专业经验,而成为Web分析行业最权威的衡量标准。通过其提供的按需服务和各种软件产品,WebTrends帮助用户获得精确的、可被 执行的Web分析数据。

webtrends 25 Tools to Improve Your Websites Usability

Website Optimiser

网站优化工具是Google免费的网站测试和优化工具。使用该工具,无需投入任何费用就可以提高现有网站的价值和点击量。通过使用网站优化工具对网 站内容和设计进行测试和优化,不管您是营销新手还是专家,都可以轻松快捷地增加收入和投资回报率。可以进行A/B实验和多版本实验两种实验。

websiteoptimize 25 Tools to Improve Your Websites Usability

这些都是非常有用的工具,如果你发现了更好的,记得与我们分享:)

编译来源:25个提高网站可用性和转化率的工具

原文来自:25 Tools To Improve Your Website’s Usability

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

mikel阅读(1074)

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

前不久看见一篇文章:在ASP.NET中使用Response.Filter 过滤网站敏感字符的解决方案。于是我借题发挥用Response.Filter 为MVC2.0 进行多国语言本地化。如果存在不足的地方,希望您指出。

本文主要给出具体思路,希望能给读者带来一定的启发:日常开发中不是所有的方案要中规中矩用常用方法解决问题。比如本文的本地化就不用resource文件来处理。

具体步骤:

一、建立自定义的LocalizationHandler类


LocalizationHandler 继承System.IO.Stream类 ,LocalizationHandler实例化后赋值给Response.Filter。这里主要通过Response.Filter来本地化MVC2.0程序。具体的Response.Filter 用法请参看MSDN.代码如下:

001 public class LocalizationHandler : Stream
002 {
003
004 private Stream responseStream;
005
006
007 public LocalizationHandler(Stream inputStream)
008 {
009 responseStream = inputStream;
010 }
011
012 public override bool CanRead
013 {
014 get { return true; }
015 }
016
017 public override bool CanSeek
018 {
019 get { return true; }
020 }
021
022 public override bool CanWrite
023 {
024 get { return true; }
025 }
026
027 public override void Flush()
028 {
029 responseStream.Flush();
030 }
031
032 public override long Length
033 {
034 get { return 0; }
035 }
036
037 long postion;
038 public override long Position
039 {
040 get
041 {
042 return postion;
043 }
044 set
045 {
046 postion = value;
047 }
048 }
049
050 public override int Read(byte[] buffer, int offset, int count)
051 {
052 return responseStream.Read(buffer, offset, count);
053 }
054
055 public override long Seek(long offset, SeekOrigin origin)
056 {
057 return responseStream.Seek(offset, origin);
058 }
059
060 public override void SetLength(long value)
061 {
062 responseStream.SetLength(value);
063 }
064
065 public override void Write(byte[] buffer, int offset, int count)
066 {
067 string sBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);
068 string pattern = @"(<|<)=(.*?)(>|>)";//正则替换类似页面格式为这样的字符串如:<=OtherContent>
069 sBuffer = Regex.Replace(sBuffer, pattern, delegate(Match c)
070 {
071 return ReadLocalizationResource().FirstOrDefault(d => d.Key == c.Groups[2].Value).Value;
072 });
073
074 ReadLocalizationResource();
075 byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(sBuffer);
076 responseStream.Write(data, 0, data.Length);
077 }
078
079 ObjectCache cache = MemoryCache.Default;
080 private Dictionary<string, string> ReadLocalizationResource()
081 {
082 string _XMLPath = "";
083
084 Dictionary<string, string> cacheData = null;
085 if (cacheData != null)
086 {
087 return cacheData;
088 }
089 Dictionary<string, string> cachedData = new Dictionary<string, string>();
090
091 string serverPath = System.Web.HttpContext.Current.Server.MapPath("~");
092 _XMLPath = Path.Combine(serverPath, "LocalizationResource.xml");
093
094 //建立缓存(使用.net4.0最新缓存机制:System.Runtime.Caching;)
095 if (cache["myCache"] == null)
096 {
097 CacheItemPolicy policy = new CacheItemPolicy();
098 policy.SlidingExpiration = TimeSpan.FromMinutes(60);
099 policy.ChangeMonitors.Add(new HostFileChangeMonitor(new List<string> { _XMLPath }));
100
101 var items = XElement.Load(_XMLPath).Elements("Module").Elements("item");
102 foreach (var item in items)
103 {
104 string key = item.Attribute("name").Value;
105 string value = item.Value;
106 cachedData.Add(key, value);
107 }
108 cache.Set("myCache", cachedData, policy);
109
110 return cachedData;
111
112 }
113
114 return (Dictionary<string, string>)cache["myCache"];
115 }
116 }

代码中的65行开始,是本地化核心代码,在这里我们用正则匹配文本。用.NET4.0 System.Runtime.Caching;(尝鲜)缓存机制提高程序执行效率。

二、修改global.asax文件


在global.asax中加入以下代码:

1 protected void Application_BeginRequest(Object sender, EventArgs e)
2 {
3 Response.Filter = new LocalizationHandler(Response.Filter);
4 }

三、建立自定义的XML本地化资源文件


截图如下:

OK,一切准备就绪,我们在不用Response.Filter 过滤的情况下,运行截图如下:

使用上文中Response.Filter过滤后:

结果将第三点中的XML作为传统的资源文件后本地化了MVC View 页面。

四、小结


本文用另外一套方案解决了MVC2.0程序的本地化问题,也适用于ASP.NET webform。同时本文还存在很多不足的地方,比如后台异步的JSON格式本地化、用户自定义本地化语言等将会在MVC2.0本地化(另类解决方 案)<下> 文中得到完善。

[转载]System.Web.Routing入门及进阶

mikel阅读(1165)

[转载]System.Web.Routing入门及进阶 上篇 – 重典的博客 – 博客园.

System.Web.Routing已经作为一个程序集包含在.net3.5sp1中发布了。虽然我们并没有在3.5sp1中发现ASP.NET Mvc的踪迹,但是亦以感觉到它离我们不远了。

System.Web.Routing用于在ASP.NETWeb应用程序中进行URLRouting

所谓UrlRouting就是将一个地址映射为另一个地址,比如我访问/chsword/2008/08/27.html其实是访问了 /chsword/article.aspx?y=2008&m=08&d=27这个地址,URLRouting使我们的URL看起来非 常漂亮。

本文将分2部分介绍UrlRouting的使用分别为入门篇与进阶篇。

文章的前提:

1. 安装Visual Studio 2008 sp1或其它IDE的等同版本。

2. 建立一个ASP.NET Web ApplicationAsp.net Web应用程序)

3. 引用System.Web.Routing程序集。

UrlRouting的实现原理

如果你不是追求理论的人,仅仅是务实主义,可以直接从准备工作读起。

为了方便大家理解我绘制了一个UML图,其中通过RouteTableRoutes这个属性可以获取一个RouteCollectionSingleton模式,虽然在其中并没有判断值不存在时才初始化的Singleton的标志性行为,但是它是在Application_Start事件中进行初始化的,并且直到应用程序进程终结,所以是Singleton模式的。

而通过以下方式将Route添加到RouteTable.Routes

RouteTable.Routes.Add(new Route());

以上代码仅为表示其流程,这个代码是不能正确执行的,因为Route没有提供无参构造函数。

Route初始化时则是利用RouteValueDictionary来加入默认值及规则到Route

另外Route还有一个IRouteHandler的实现对象,IRouteHandler的实现对象提供了一个GetHttpHandler方法来获取用于处理URLIHttpHandler

这么说还是停留在抽象层次的,下面我们以一些简单例子来带你驭起UrlRouting

准备工作

由于须要一个处理UrlIHttpHandler所以我们先定义一个实现了IHttpHandler接口的类。

就叫它MyPage,由于我们要与IRouteHandler交互,所以除了实现IHttpHandler的方法之外还要声明一个RequestContext类型的属性

public class MyPage:IHttpHandler {
public RequestContext RequestContext { get; private set; }
public MyPage(RequestContext context)
{
this.RequestContext = context;
}
#region IHttpHandler 成员
public virtual void ProcessRequest(HttpContext context){}
public bool IsReusable {
get { return false; }
}
#endregion
}

这样我们就拥有了一个自己的IHttpHandler

下面我们实现一个IRouteHandler

public class MyRouteHandler : IRouteHandler {
#region IRouteHandler 成员
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new MyPage(requestContext);
}
#endregion
}

这里实现了IRouteHandlerGetHttpHandler方法,使之返回刚才我们实现的MyPage

这样我们前期的2个工作就做完了,我们可以来实现UrlRouting了。

实现第一个UrlRouting

其实UrlRouting在定义完上两个规则后就很简单了。

Golbal.asax(没有可以新建一个)的Application_Start事件中写如下代码

protected void Application_Start(object sender, EventArgs e) {
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes) {
routes.Add(
new Route({page}.aspx,new MyRouteHandler()));
}

这样我们就定义了第一个UrlRouting规则就是对xxxx.aspx这类的Url进行Routing

但是我们仅仅是定义了处理了什么样的Url,却没定义如何处理。

我们应该在刚刚定义的MyPageProcessRequest方法中定义如何处理。

我们将ProcessRequest方法实现如下:

public virtual void ProcessRequest(HttpContext context){
context.Server.Execute(        RequestContext.RouteData.Values[
page].ToString().Replace(_,/)+.aspx
);
}

很显然这里的RequestContext.RouteData.Values[“page”]就是取到刚才的规则{page}.aspx中的page的值即,如果我访问index.aspxRequestContext.RouteData.Values[“page”]就是index

我这里的定义是将”_”替换为”/”所以就是将list_index.aspx这样的URL转到list/index.aspx这样的网页上。

我们建立一些测试用的网页如下图所示:

在这些网页里随意写些可以分辨网页的文字。

则访问list_chsword.aspx时自动Routing到了list/chsword.aspx上了

总结一下UrlRouting与以下有关:

1. Application_Start中定义的规则

2. 自己实现的IHttpHandler

这样您对于UrlRouting就算是入门了,下一篇我们将来讲一些进阶设置。

预计内容:

  • 规则的默认值
  • 正则规则设置
  • 使用技巧

上面介绍的是最简单的一种定义方式。当然我们可以建立更复杂的规则。其中就包括设定规则的默认值以及设定规则的正则表达式。

UrlRouting高级应用

预计效果:

当我访问/a/b.aspx时就会转到Default.aspx?category=a&action=b在页面上显示
category:a
action:b

亦如果我访问/chsword/xxxx.aspx就会转到Default.aspx?category=chsword&action=xxxx就会显示
category:chsword
action:xxxx

如果访问/chsword/就会转到 Default.aspx?category=chsword&action=index就会显示

category:chsword

action:index

首先我建立一个Route

routes.Add(
Default,
new Route({category}/{action}.aspx,
new RouteValueDictionary(
new
{
file
= Default,
category
= home,
action
= index
}),
new MyRouteHandler()
)
);

当然IHttpHandler的处理方式也要有所改变

为了方便查看我使用了下方法:

context.Server.Execute(string.Format(/{0}.aspx?category={1}&action={2},
RequestContext.RouteData.Values.ContainsKey(
file)
? RequestContext.RouteData.Values[file].ToString()
:
default,
RequestContext.RouteData.Values.ContainsKey(
category)
? RequestContext.RouteData.Values[category].ToString()
:
“”,
RequestContext.RouteData.Values.ContainsKey(
action)
? RequestContext.RouteData.Values[action].ToString()
:
“”)
);

即/a/b.aspx是映射到Default.aspx?category=a&action=b

在Default.aspx中写如下代码:

category:<%=Request.Params[category] %><br />
action:
<%=Request.Params[action] %>

以显示传入的参数。

如果在IIS中设置Index.aspx时就算输入/a/也会访问到/a/index.aspx,即默认的会按RouteValueDictionary中设置的值自动补全

UrlRouting使用正则表达式规则

UrlRouting在定义的时候也可以按正则的规则来进行定义。

routes.Add(
zz,
new Route({category}/{action}.chs,
new RouteValueDictionary(
new {
file
= Default,
category
= home,
action
= 1
}),
new RouteValueDictionary(
new {
action
= [\\d]+
}),
new MyRouteHandler()
)
);

以上代码规定了action只能是数字则访问/a/1.chs可以正常访问。

而访问/a/b.chs则会显示未找到资源。

当然这是里可以使用更高级的正则表达式。

UrlRouting的技巧

排除UrlRouting的方法:

System.Web.Routing默认提供了一个IRouteHandlerStopRoutingHandler Class,经过它处理的URL不会被做任何处理

通常使用方法如下:

routes.Add(new Route(“{resource}.axd/{*pathInfo}”, new StopRoutingHandler()));

RouteHandler工厂:

其实IRouteHandler可以实现一个RouteHandler的简单工厂。

public class RouteHandlerFactory : IRouteHandler
{
string Name { get; set; }
public RouteHandlerFactory(string name){this.Name = name;}
#region IRouteHandler 成员
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
if (this.Name == mypage)
return new MyPage(requestContext);
if(this.Name=mypage1)
return new MyPage1(requestContext);
}
#endregion
}

规定HTTP verbs,这里要使用System.Web.Routing中的HttpMethodConstraint

void Application_Start(object sender, EventArgs e) {
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes){
string[] allowedMethods = { GET, POST };
HttpMethodConstraint methodConstraints
= new HttpMethodConstraint(allowedMethods);
Route reportRoute
= new Route({locale}/{year}, new ReportRouteHandler());
reportRoute.Constraints
= new RouteValueDictionary { { httpMethod, methodConstraints } };
routes.Add(reportRoute);
}

Demo程序代码下载:

http://files.cnblogs.com/chsword/WebApplication3.rar

[原创]VS2008源代码与原始版本不同解决办法

mikel阅读(1669)

最近调试程序设置断点后遇到“源代码与原始版本不同解决办法”的提示,在网上也找了很多解决办法,一一尝试,问题解决

我无法设定断点的原因是因为从d:拷贝项目文件夹到了F:盘,我是通过断点窗口的提示发现的问题,

在VS2008中”调试”–“窗口”–“断点” 显示出断点窗口,然后点击左上角的”新建” 中的”在函数处中断”,然后再弹出窗口中输入函数名,当你运行后,会提示“源代码与原始版本不同解决办法”的窗口,让你是否按源文件执行,你选择否,你就会看到一个文件浏览窗口,上方显示原文件位置,这里是原文件的位置路径,我发现引用的还是d:盘的路径,现在已经拷贝到F:盘,于是我想到把所有解决方案中的项目引用dll都重新删除再引用新的路径的对应dll文件,然后清理缓存文件,重新编译解决方案,问题解决

特此总结一下解决方法:

1.删除无法设定断点项目文件夹下的obj目录,删除C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\对应解决方案的文件夹

2.删除所有原始引用的dll,然后再引用现在新路径的dll

3.重新生成解决方案

[转载]ASP.NET MVC & EF 构建智能查询 二、模型的设计与ModelBinder

mikel阅读(919)

[转载]ASP.NET MVC & EF 构建智能查询 二、模型的设计与ModelBinder – 重典的博客 – 博客园.

在第一篇中,我讲解了我们要做智能查询的原因,以及基本的解决方案设计。从这篇开始我们开始讲解它的实现过程。

其实在写这一系列文章之初,我其实是想由底至上去讲解,但是我又整理了一遍代码才发现,其实如果不了解最表面的东西,也是不太好深入的。

所以我们的第二篇文章就来讲一下我们这个智能查询框架中最浅,但也是使用最频繁的部分,也就是Model。

首先我们的Entity  或者说数据库的结构如下

image

另外如下面代码,我们有一个用于传递name=value对,及查询谓词的model

   1: public ActionResult Index(QueryModel model)
   2: {
   3:     using(var db=new DbEntities())
   4:     {
   5:         var list = db.Users.Where(model).ToList();
   6:         return View(list);
   7:     }
   8: }

我命名之为QueryModel。它由Action的参数传入,再传入EF的Where扩展方法,它是构建Lambda表达式的原型,上面是我们的一个通用的查询Action的代码。

而QueryModel的代码为

image

QueryModel的唯一一个属性Items,是一个ConditionItem的集合,它里面存着所有的查询条件。

而ConditionItem里面的属性

  1. Field表示要查询的目标属性的名称,我们的设定它是支持子属性查询的,例如 “Profile.MyUser.Id”
  2. Method则是当前使用的谓词,它是QueryMethod的一个枚举值
  3. OrGroup是一个字符串,是一个OrGroup的多个表达式将会以Or操作符进行关联,然后再And
  4. Prefix是一个分类的前缀,我们假定一个Action可能处理多个查询条件组的时候为了分开这些查询条件而加的属性
  5. Value则是这个表达试的值

而我们在页面上类似

   1: <form action="" method="post">
   2: 姓名:<input id="Name" name="[Like]Name" type="text" value="" />
   3: Email:<input id="Email" name="[Equal]Email" type="text" value="" /><br />
   4: Id: <input id="Id" name="[Equal]Id" type="text" value="" />
   5: 生日: <input id="Birthday" name="[Equal]Birthday" type="text" value="" /><br />
   6: <input type="submit" value="查询" />
   7: </form>

这样的表单,我们提交到服务器端,我们要进行ASP.NET MVC 中IModelBinder的转换,要将querystring 或 postdata中的KeyValuePair转换为我们的ConditionItem。

当然,我们除了谓词Method之外,还有Prefix以及OrGroup要从客户端提交过来所以我们就要制定一个规则

我们约定

  1. 中括号[]中的为查询谓词
  2. 小括号()中的为前缀Prefix
  3. 大括号{}中的为OrGroup

我们可以通过以下IModelBinder的实现将Request.QueryString或Request.Form中的值转换到QueryModel中:

   1: public class SearchModelBinder : IModelBinder
   2:    {
   3:        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   4:        {
   5:            var model = (QueryModel)(bindingContext.Model ?? new QueryModel());
   6:            var dict = controllerContext.HttpContext.Request.Params;
   7:            var keys = dict.AllKeys.Where(c => c.StartsWith("["));//我们认为只有[开头的为需要处理的
   8:            if (keys.Count() != 0)
   9:            {
  10:                foreach (var key in keys)
  11:                {
  12:                    if (!key.StartsWith("[")) continue;
  13:                    var val = dict[key];
  14:                    //处理无值的情况
  15:                    if (string.IsNullOrEmpty(val)) continue;
  16:                    AddSearchItem(model, key, val);
  17:                }
  18:            }
  19:            return model;
  20:        }
  21:
  22:        /// <summary>
  23:        /// 将一组key=value添加入QueryModel.Items
  24:        /// </summary>
  25:        /// <param name="model">QueryModel</param>
  26:        /// <param name="key">当前项的HtmlName</param>
  27:        /// <param name="val">当前项的值</param>
  28:        public static void AddSearchItem(QueryModel model, string key, string val)
  29:        {
  30:            string field = "", prefix = "", orGroup = "", method = "";
  31:            var keywords = key.Split(']', ')', '}');
  32:            //将Html中的name分割为我们想要的几个部分
  33:            foreach (var keyword in keywords)
  34:            {
  35:                if (Char.IsLetterOrDigit(keyword[0])) field = keyword;
  36:                var last = keyword.Substring(1);
  37:                if (keyword[0] == '(') prefix = last;
  38:                if (keyword[0] == '[') method = last;
  39:                if (keyword[0] == '{') orGroup = last;
  40:            }
  41:            if (string.IsNullOrEmpty(method)) return;
  42:            if (!string.IsNullOrEmpty(field))
  43:            {
  44:                var item = new ConditionItem
  45:                               {
  46:                                   Field = field,
  47:                                   Value = val.Trim(),
  48:                                   Prefix = prefix,
  49:                                   OrGroup = orGroup,
  50:                                   Method = (QueryMethod) Enum.Parse(typeof (QueryMethod), method)
  51:                               };
  52:                model.Items.Add(item);
  53:            }
  54:        }
  55:    }

当然我们还要在Global.asax中添加

   1: ModelBinders.Binders.Add(typeof (QueryModel), new SearchModelBinder());

这样我们就可以在Action中获取到相应的查询条件了

image

我们可以看到,从表单提交过来的数据我们已经正确地存放在QueryModel中了。

[转载]ASP.NET MVC & EF 构建智能查询 一、智能查询的需求与设计

mikel阅读(833)

[转载]ASP.NET MVC & EF 构建智能查询 一、智能查询的需求与设计 – 重典的博客 – 博客园.

关于复用

在我们日常的开发过程中,代码的复用其实是很重要的一部分,ASP.NET MVC框架本身为我们提供了很多很好的复用机制,让我们能充分地利用它们来节省我们的Coding成本。

在简单的Coding中,我们可以通过构造方法来实现代码段的复用,在OOP编程中我们可以使用继承多态来进行类的复用,我们也可以使用设计模式来做类或对象间的代码设计的复用,随着程序的复杂我们就想构造出更佳的复用方式,可以向更高层次上抽象。

应用场景与目标

在信息管理系统中我们会开发大量的List页面,它们功能上通常是非常相似的,一般是包含一个查询条件组和一个列表。

例如下图所示

image

那我的目标呢,就是对这里面的查询功能进行封装,以达到“只要更改页面上的条件,就可以实现自动的查询逻辑”这样的功能,即:如果我们要加一个查询条件则不需要修改逻辑代码只修改页面即可。

其实对于一个有经验的开发人员呢,根据HTML页面的查询条件去自动生成查询结果并不是难事。最简单的是将查询提交表单遍历,然后连接成一个SQL进行查询。

但是使用SQL有很多问题

  1. 注入问题,因为不知道类型,所以很多地方都要拼写引号就算是使用Parameter,也无法处理类型转换的问题
  2. 无法处理Or和And共存的问题
  3. 无法处理其它查询条件如大于小于Like

那我们想一下我们使用EF的话是如何正常去编写一个查询的呢

我们通常是在逻辑中去拼SQL或是写如下的EF:

   1: var query = db.User.AsQueryable();
   2: if (string.IsNullOrEmpty(name))
   3:     query = query.Where(c => c.Name == name);
   4: if (id.HasValue)
   5:     query = query.Where(c => c.Id == id);
   6: if (string.IsNullOrEmpty(email))
   7:     query = query.Where(c => c.Email == email);
   8: return query.ToList();

那我们的目标其实就明确了,我们就是要让代码自动去完成上述这一过程,并且可以支持

  1. 识别类型(EF的话这个很重要)
  2. 仅查询有效条件
  3. 处理可空类型
  4. 处理类型转换(如DateTime与UnixTime的转换)
  5. 一些代码性逻辑(比如查询某些日期的时候,其实我们要的结果并不是c=>c.Time==time而是c=>c.Time>time && c.Time<time.AddDays(1))
  6. 支持Or等 混合查询
  7. 关于Like的相关处理
  8. 关于In操作的相关处理
  9. Join查询的相关处理

理论流程

看一看我们要做的其实还挺多的,但是我已经在心里搭出了一个这样的流程,使用ASP.NET MVC及EF特性来完成这些功能

image

那其实呢首先的问题就是我们从浏览器请求向服务器的时候,除了要查询的字段,还应该包含有 怎么查(就是操作符,比如= < > like in)、是否为Or、

而HTML中唯一可以直接传到服务器的就是表单元素的name所以我们可以对name来做一些手脚让它包含这些信息

比如[Equal]Id就表示让Id这个属性的值等于这个表单项的Value

[Equal]Id=1

我们就利用这个postdata或QueryString去构造出

c=>c.Id==1的Lambda表达式,并且支持多个条件

实际实现

而我是在Helper上加了一层扩展来实现这样的功能的,形如

   1: <form action="" method="post">
   2: 姓名:@Html.TextBox("Name").ForSearch(QueryMethod.Like)
   3: Email:@Html.TextBox("Email").ForSearch(QueryMethod.Equal)<br />
   4: Id: @Html.TextBox("Id").ForSearch(QueryMethod.Equal)
   5: 生日: @Html.TextBox("Birthday").ForSearch(QueryMethod.Equal)<br />
   6: <input type="submit" value="查询" />
   7: </form>

这里我们使用ForSearch这个扩展为Html的name添加了前置的谓词标记[Equal]

我们在服务器端只要统一处理

   1: public ActionResult Index(QueryModel model)
   2: {
   3:     using(var db=new DbEntities())
   4:     {
   5:         var list = db.Users.Where(model).ToList();
   6:         return View(list);
   7:     }
   8:
   9: }

我们是在这个Where的扩展方法里重写了将QueryModel转换成lambda表达式的工作,当然在这之前我们还利用了自已实现的ModelBinder去将 postdata/querystring进行了分析前初始化了QueryModel

在未来的几篇中我们将会详述这些过程的实现

[转载]c#进阶-方法中的ref和out

mikel阅读(1139)

[转载]c#进阶-方法中的ref和out – 撞破南墙 – 博客园.

在一个方法中 你必须知道每个参数是引用类型还是值类型否则你将发现你无法控制程序。

这些细节并不是每个.NET程序员都需要知道,但是却是进阶必备的,差距也在于此。

C#中默认是使用值类型,你可以使用REF 和OUT 来使之变成引用类型。

在CLR中只有1个bit去区分它们是ref还是out。如果使用的是out 那么

1你不能够在赋值前使用它,因为他还未赋值。

2在方法返回的时候你必须为他赋值

如果是ref  你可以直接读写他。

REF 和 OUT 都使得方法中的参数把值带回来,不同的在于编译器把对他们进行了检查,如下面的情况。

public sealed class Program {
public static void Main() {
Int32 x;
// x is not initialized.
// The following line fails to compile, producing
// error CS0165: Use of unassigned local variable ‘x’.
AddVal(ref x);
Console.WriteLine(x);
}
private static void AddVal(ref Int32 v) {
v
+= 10; // This method can use the initialized value in v.
}

C#中为什么一定要申明是ref 还是 out ,如果不申明编译一样没有问题,在C++中就没有ref 和out这回事。

应该是C#的设计者认为开发者应该应该清楚的知道自己传递的参数的状态作用。

在c#中,允许以下的重载。

public sealed class Point {
static void Add(Point p) { … }
static void Add(ref Point p) { … }
}

但是不允许同时使用 REF和OUT的重载。因为元数据表示两个方法的签名是一样的。

image

让两个引用类型的变量交换

public static void Swap(ref Object a, ref Object b) {
Object t
= b;
b
= a;
a
= t;
}
//To swap references to two String objects, you’d probably
//think that you //could write code     like this:

public static void SomeMethod() {
String s1
= Hello;
String s2
= World;
// Variables that are passed by reference
// must match what the method expects.
Object o1 = s1, o2 = s2;
Swap(
ref o1, ref o2);
// Now cast the objects back to strings.
s1 = (String) o1;
s2
= (String) o2;
//
Console.WriteLine(s1); // Displays “World”
Console.WriteLine(s2); // Displays “Hello”
}

如果Swap(ref s1, ref s2); 似乎没有问题,但是

image

对于ref 他是执行了类型检查的。

[转载]Ruby on Rails 体验之旅(一)--安装

mikel阅读(1028)

[转载]Ruby on Rails 体验之旅(一)–安装 – PurpleCow – 博客园.

最新心血来潮的想学一门新的东西,听说用ROR的开发效率会很高,所以就选择了ruby on rails,安装的时候真是不顺心,这里把自己的安装心得记下来:

1) 安装ruby

    下载安装包:http://rubyforge.org/frs/?group_id=167,我下载的是rubyinstaller-1.8.7-p302.exe

    装完后,可以用ruby -v 测试是否安装成功,如图,要是出现了版本 就说明安装成功了

image

2)安装rubygems

Rubygems是ruby的包管理器工具,它使得ruby包的安装十分简单,只需要一条命令就可以从远程服务器上下载相应的包,如果相应的应用包含其他 扩展,rubygems 也会提示你从远程安装所依赖的扩展。安装后 rubygems 会运行相应的程序生成 rdoc 帮助文档(类似于 javadoc )。目前已经成为 ruby 事实上的包管理器标准了。

下载地址:http://rubyforge.org/projects/rubygems/

我下载的是rubygems-1.3.7,解压后执行目录下的setup.rb,安装完成以后,用如下命令检查:

gem –v

image

3、安装rails

a)远程安装:gem install rails –remote

下面来自于网上的说明我没有采用这种方式安装,因为我家 的网速太慢了。

然后会自动输出:
Install required dependency activerecord?
Install required dependency actionpack?
Install required dependency actionmailer?
Install required dependency activeresource?
依次yes依赖文件,然后显示:
Successfully installed rails-2.1.0
Successfully installed activerecord-2.1.0
Successfully installed actionpack-2.1.0
Successfully installed actionmailer-2.1.0
Successfully installed activeresource-2.1.0
Installing ri documentation for activerecord-2.1.0…
Installing ri documentation for actionpack-2.1.0…
Installing ri documentation for actionmailer-2.1.0…
Installing ri documentation for activeresource-2.1.0…
Installing RDoc documentation for activerecord-2.1.0…
Installing RDoc documentation for actionpack-2.1.0…
Installing RDoc documentation for actionmailer-2.1.0…
Installing RDoc documentation for activeresource-2.1.0…
看看版本:rails -v
输出:Rails 2.1.0
搞定

b)本地手动安装:

需要下载

activesupport

http://files.rubyforge.vm.bytemark.co.uk/activesupport/activesupport-2.2.3.gem

activerecord

http://files.rubyforge.vm.bytemark.co.uk/activerecord/activerecord-2.2.3.gem

actionpack

http://files.rubyforge.vm.bytemark.co.uk/actionpack/actionpack-2.2.3.gem

actionmailer

http://rubyforge.org/frs/download.php/64424/actionmailer-2.2.3.gem

rake
http://files.rubyforge.vm.bytemark.co.uk/rake/rake-0.8.7.gem

activeresource

http://rubyforge.org/frs/download.php/64419/activeresource-2.2.3.gem

rails     http://rubyforge.org/frs/download.php/64426/rails-2.2.3.gem

下载完成之后将所有文件放在同一个文件夹下面依次安装:(eg:D:\ruby)

然后执行命令

(D: –> D:\>cd D:\ruby)

gem install  activesupport

gem install  activerecord

gem install  actionpack

gem install  actionmailer

gem install  rake

gem install  rails

成功安装之后检查命令

rails –v

image

证明安装成功

4、创建Web应用

在命令行下输入 rails HelloWorld,比如当前的路径是 E:\Ruby on Rails>rails HelloWorld,便会在E:\Ruby on Rails下创建一个HelloWorld文件夹。

image

如果需要指定路径可以rails C://test,具体可以看官方API:http://api.rubyonrails.org/

5、在当前目录(HelloWorld)下,运行 ruby script\server 启动服务,关于webrick服务器更多的信息可以访问 http://www.webrick.org/

image

6、浏览器中输入http://localhost:3000/,如果看到以下页面,说明OK

image

7、写一个HelloWorld的Web程序,ruby script\generate controller HelloWorld命令

image

然后找到hello_world_controller.rb

    1. class HelloWorldController < ApplicationController
    2. def index
    3. render:text=>”hello world”
    4. end
    5. end
  • 输入以下代码,保存

  • 如果未启动服务需要重启第五步操作。

在浏览器中打开http://localhost:3000/hello_world,就会看见结果,但我这里出现一个错误:

image

    在网上看到一个解决方法:

    这是由于没有装SQLite3数据库,http://files.cnblogs.com/geek007/sqlite3.rar 这里下载,

    然后解压到ruby的bin目录:D:\ruby\bin,命令行执行:gem install “D:\Program Files\Ruby187\bin\SQLite3-ruby-1.2.3-mswin32.gem”

image

安装成功后,按照第五步重启服务器,在浏览器中访问

http://localhost:3000/hello_World

image

Ok,大功告成~呵呵,开心~~~

PS: 手工安装非常麻烦,可以从 http://instantrails.rubyforge.org/ 下载InstantRails,全部搞定。

参考资源:

http://rubyforge.org

http://api.rubyonrails.org/

[转载]不固定参数的存储过程

mikel阅读(1073)

[转载]不固定参数的存储过程 – KenshinCui’s Blog – 博客园.

我们知道存储过程是不支持不固定参数情况的(包括数组参数),可是有时候我们的参数又必须是不固定的,怎么办呢?我想此时不妨使用字符串参数来帮助我们解决这种情况,利用字符串分割的方法将一个参数分割成数个参数来解决。下面我们看一个例子:

假设现在给你一个产品信息列表(显示出各个商品的基本信息),现在我想要根据所选择商品进行统计(任意选择几种),例如统计出价 格<10,11-20,21-30,31-40,41-50,50以上的商品个有多少个(姑且认为就统计这些)。此时如果使用存储过程就势必需要传 入所选商品的id作为参数,但是id个数是不固定的。此时估计会有人这样写:

代码

SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: jianxin160 -- Create date: 2010.11.05 -- Description: 统计商品 -- ============================================= ALTER PROCEDURE StatProductInfo ( @ids VARCHAR(8000) ) AS BEGIN DECLARE @followingTen INT DECLARE @elevenToTwenty INT DECLARE @twentyOneToThirty INT DECLARE @thirtyOneToFourty INT DECLARE @fourtyOneToFifty INT DECLARE @fiftyOrMore INT SELECT @followingTen=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice<10 SELECT @elevenToTwenty=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice BETWEEN 11 AND 20 SELECT @twentyOneToThirty=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice BETWEEN 21 AND 30 SELECT @thirtyOneToFourty=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice BETWEEN 31 AND 40 SELECT @fourtyOneToFifty=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice BETWEEN 41 AND 50 SELECT @fiftyOrMore=COUNT(*) FROM dbo.Products WHERE ProductID IN(@ids) AND UnitPrice>50 SELECT @followingTen AS '<$10',@elevenToTwenty AS '$11-$20', @twentyOneToThirty AS '$21-$30',@thirtyOneToFourty AS '$31-$40', @fourtyOneToFifty AS '$41-$50',@fiftyOrMore AS '>$50' END GO

其实如果你测试一下(例如:EXEC dbo . StatProductInfo ‘3,4,8,10,22’ )是有问题的,SQL server认为这整个是一个参数,转换时出错。此时我们想一下如果这些字段在一个虚表中就容易操作多了,但是一般虚表是有其他表通过查询得到,现在根本 无法查询又哪来的虚表呢?聪明的朋友或许已经想到可以使用”表值函数”。对,答案就是使用”表值函数”。我们知道”表值函数”可以返回一个”Table” 类型的变量(相当于一张虚表,存放于内存中),我们首先将字符串分割存放到”表值函数”的一个字段中,然后我们再从”表值函数”中查询就可以了(这个例子 也是”表值函数”的一个典型应用)。具体SQL如下:

代码

SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: cmj -- Create date: 2010.11.05 -- Description: 返回一个Table,只有一列,每一行的数据就是分割好的字符串 -- ============================================= CREATE FUNCTION GetSplitFieldsByString ( @toSplitString varchar(1000), @splitChar varchar(10) ) RETURNS @tb TABLE(sp varchar(100)) AS BEGIN DECLARE @i INT SET @toSplitString=RTRIM(LTRIM(@toSplitString)) SET @i=CHARINDEX(@splitChar,@toSplitString) WHILE @i>0 BEGIN INSERT @tb VALUES(LEFT(@toSplitString,@i-1)) SET @toSplitString=RIGHT(@toSplitString,LEN(@toSplitString)-@i) SET @i=CHARINDEX(@splitChar,@toSplitString) END IF LEN(@toSplitString)>0 INSERT @tb VALUES(@toSplitString) RETURN END GO

然后我们稍微修改一下存储过程:

代码

SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: jianxin160 -- Create date: 2010.11.05 -- Description: 统计商品 -- ============================================= ALTER PROCEDURE StatProductInfo ( @ids VARCHAR(8000) ) AS BEGIN DECLARE @followingTen INT DECLARE @elevenToTwenty INT DECLARE @twentyOneToThirty INT DECLARE @thirtyOneToFourty INT DECLARE @fourtyOneToFifty INT DECLARE @fiftyOrMore INT SELECT @followingTen=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice<10 SELECT @elevenToTwenty=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice BETWEEN 11 AND 20 SELECT @twentyOneToThirty=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice BETWEEN 21 AND 30 SELECT @thirtyOneToFourty=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice BETWEEN 31 AND 40 SELECT @fourtyOneToFifty=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice BETWEEN 41 AND 50 SELECT @fiftyOrMore=COUNT(*) FROM dbo.Products WHERE ProductID IN(SELECT sp FROM dbo.GetSplitFieldsByString(@ids,',')) AND UnitPrice>50 SELECT @followingTen AS '<$10',@elevenToTwenty AS '$11-$20',@twentyOneToThirty AS '$21-$30', @thirtyOneToFourty AS '$31-$40',@fourtyOneToFifty AS '$41-$50',@fiftyOrMore AS '>$50' END GO

这样通过执行EXEC dbo . StatProductInfo ‘3,4,8,10,22’ 就可以得到想要的结果了:

[转载]在ASP.NET MVC2.0使用Lodop为WEB打印提出完美解决方案

mikel阅读(1055)

[转载]在MVC2.0使用Lodop为WEB打印提出完美解决方案 – RyanDing – 博客园.

通过好友CallHot介绍Lodopweb打印控件。由于是国人开发的,故这两天认真了研究下,打算在未来的项目中使用。现将学习成果与园友分享。如果存在不足的地方,希望您指出。

具体的实现步骤如下:

一、准备工作


1.MVC2.0 + JQuery1.4.1 开发环境。

2.Lodop web 打印控件,官方地址:http://mtsoftware.v053.gokao.net/download.html  (注:国人开发,免费软件)。

3.StringTemplate,C#开源模板引擎。官方地址:http://www.stringtemplate.org。

本文主要给出WEB下打印步骤实现方案,具体的技术实现细节,请查看官方API。lodop,stringtemplate 官方已给出了详尽的文档说明。

二、MVC2.0使用StringTemplate构造打印模板


StringTemplate 文中简称st。网络上有相关文档介绍st效率还不错。本文将st作为报表打印模板。在实际项目开发中将繁杂的报表打印工作内容,部分分配给美工来处理。而 开发人员只需提供数据源接口。使用st可以减轻开发人员的工作量。并将报表开发任务分工更细致。给项目带来的好处就不多论了。具体实现如下:

1.在MVC2.0项目中引用st核心dll:

2.建立st的模板文件,template.st(st模板专用文件):

也可以认为st文件就是一个普通的html文件。该部分主要由美工负责处理,比如CSS。

3.在MVC2.0 controller 内建立提供数据源的 JsonResult:

01 public JsonResult Print()
02 {
03 //构造打印数据
04 List<CustomerTest> list = new List<CustomerTest>();
05 for (int i = 0; i < 100; i++)
06 {
07 list.Add(new CustomerTest { CustomerName = "candy" + i, CustomerAddress = "思明区" + i, CustomerPhone = "13148484855" + i });
08 list.Add(new CustomerTest { CustomerName = "linda" + i, CustomerAddress = "湖里区" + i, CustomerPhone = "13847487545" + i });
09 list.Add(new CustomerTest { CustomerName = "ellie" + i, CustomerAddress = "海昌区" + i, CustomerPhone = "1359984665" + i });
10 }
11
12 //StringTemplate 打印模板文件,实际项目中为提高程序效率,应将打印模板文件缓存。
13 string serverPath = System.Web.HttpContext.Current.Server.MapPath("~");
14 string path = Path.Combine(serverPath, @"PrintTemplate\");
15
16 StringTemplateGroup group = new StringTemplateGroup("myGroup", path, typeof(TemplateLexer));
17 StringTemplate st = group.GetInstanceOf("template");
18 st.SetAttribute("customer", list);
19
20 //为打印提供html相关超文本内容。
21 StringBuilder sb = new StringBuilder();
22 sb.Append(@"<html xmlns='http://www.w3.org/1999/xhtml' lang='zh-CN'>");
23 sb.Append("<head>");
24 sb.Append(@"<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />");
25 sb.Append(@"<meta http-equiv='Content-Language' content='zh-CN' />");
26 string cssContent = System.IO.File.ReadAllText(Path.Combine(serverPath, @"Content\CSS\CSSForPrint.css"));
27 sb.Append(@"<style type='text/css'>");
28 sb.Append(cssContent);
29 sb.Append(@"</style>");
30 sb.Append("</head>");
31 sb.Append("<body>");
32 sb.Append(st.ToString());
33 sb.Append(" ");
34 sb.Append("</body>");
35 sb.Append("</html>");
36
37 return Json(new { success = true, data = sb.ToString() }, JsonRequestBehavior.AllowGet);
38 }

其中CustomerTest是自定义数据类,已经给出详细的注释了。仔细阅读不难理解。

4.MVC2.0 view html head 内加入js 代码:

01 <asp:Content ID="Content3" ContentPlaceHolderID="Head" runat="server">
02 <script language="JavaScript" src="CheckActivX.js"></script>
03 <object id="LODOP" classid="clsid:2105C259-1E0C-4534-8141-A753534CB4CA" width="0"
04 height="0">
05 </object>
06 <script type="text/JavaScript">
07
08 function prn1_preview(data) {
09 LODOP.PRINT_INIT("打印控件功能演示_Lodop功能_打印表格");
10 //报表标题
11 LODOP.ADD_PRINT_HTM(50, 300, 330, 300,
12 "<font color ='black' size ='6'>客户列表</font><font color ='blue' size ='2'>(制表人:张三)</font>");
13 //报表内容打印。
14 LODOP.ADD_PRINT_TABLE(100, 150, 760, 900, data);
15 LODOP.PREVIEW();
16 };
17
18 $(function () {
19 $("#btnPrint").click(function () {
20 var url = '<%=Url.Action("Print","Home") %>';
21 $.ajax({
22 type: "POST",
23 url: url,
24 cache: false,
25 dataType: 'json',
26 success: function (result) {
27 if (result.success) {
28 prn1_preview(result.data);
29 }
30 }
31 });
32 });
33 })
34 </script>
35 </asp:Content>


三、运行截图


最后一页打印预览:

打印机横向打印:

四、注意事项


本文给出的web打印方案需要读者对MVC2.0 、JQuery 、StringTemplate 有一定的了解。另外本例只是在IE下实现了WEB打印,如果需要Firefox或其他浏览器下支持web打印请联系Lodop作者

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