[转载]MVP模式在asp.net中的应用

mikel阅读(1032)

[转载]MVP模式在asp.net中的应用 – 奋斗的天空 – 博客园.

前段时间,由于项目中需要用到MVP模式,于是我便来博客园查了一下,发现博客园有关MVP模式的资料少之又少,加上本人在博客园潜水这么久, 也想出来透透气啦,从而产生了写博的冲动。整个项目做下来,也对MVP模式有了一定的了解,特写下这篇文章来做一个总结吧,希望对学习MVP模式的朋友有 所帮助。话不多说,直接上图:

但是本章的重点不是这张图,而是在ASP.NET项目中怎么去用MVP模式。如果您对MVP模式有所了解的话,那么接下来就要上菜啦。

下面就用一个view开始:

public interface IMvpView { }

这是一个最基本的View接口,也就是说我们的UI(page,control)会直接或间接的继承它,我们还可以在这个接口里定义一些属性和 方法,而这些属性和方法是在我们每个界面都会用到的。很简单吧!

接下来就是一个presenter的基础类:

public abstract class Presenter<TView> where TView : IMvpView { public TView View { get; set; } public virtual void OnViewInitialized() { } public virtual void Save() { } }

这是一个最基本的presenter class,我把它定义为一个抽象的范型类。这个类的内容也很简单,它有一个TView类型的View属性,注意这里的TView不应该是一个具体 View,而应该是一个View的抽象。它还包括两方法,OnViewInitialized()和Save(), 我都把它们定义为Virtual,是要在继承类中重载。其中OnViewInitialized()方法里主要执行一些当 (IsPostBack==false)的操作,当然你还可以增加一个OnViewLoad()方法,来执行当页面加载时的操作.

定义了一些最基本的,接下来我要定义一些东东继承它们。

接下来创建一个IPersonView,它继承自我们最基本的view接口IMvpView:

public interace IPersonView:IMvpView { IList<Person> Persons{get;set;}
void Save(); }

接下来我又创建了一个PersonPresenter类,它继承自Presenter抽象类

public class PersonPresenter : Presenter<IPersonView> { private readonly IPersonController _controller; public PersonPresenter(IPersonController controller) { _controller = controller; } public override void OnViewInitialized() { View.Persons= _controller.GetPersons(); } public override void Save() { //do something } }

等等,怎么会突然冒出个IPersonController,这个接口干嘛的?重点来啦,presenter凭什么可以承担起UI的所有逻辑, 就是因为有这个IPersonController接口,这个接口的主要职责是通过您的Data Layer获得presenter完成UI逻辑所需要的数据,它是presenter的强大靠山。在这个例子中,IPersonController获得 person列表,来给赋给View的Persons属性,当然还做一些保存的动作(Save()).

接下来我还要做一件很有意义的事情,就是创建一个抽象的基础页面类:

public abstract class ViewBasePage<TPresenter, TView> : Page,IMvpView where TPresenter : Presenter<TView> where TView : IMvpView { protected TPresenter _presenter; public TPresenter Presenter { set { _presenter = value; _presenter.View = (IMvpView)(object)this; } } }

所有UI都会继承这个类,虽然它现在所做的事情很简单(仅仅是做范例),当然你还可以加一些在其它界面都会用到的方法或者属性来增强这个基类。

在这个例子中,我没有去实现Data Layer和controller,当然这也不是本文的重点,我希望通过这篇文章,让学习MVP模式的朋友,能对MVP模式有一个新的认识。

[原创]百度排名日志分析

mikel阅读(1247)

LogParser的导入SQL Server的命令:

logparser -i:IISW3C "SELECT TO_LOCALTIME(TO_TIMESTAMP([date], [time])) as [logdate] , [s-sitename], [s-computername], [s-ip], [cs-method], [cs-uri-stem], [cs-uri-query], [s-port], [cs-username], [c-ip], [cs-version], [cs(User-Agent)], [cs(Cookie)], [cs(Referer)], [cs-host], [sc-status],[sc-substatus], [sc-win32-status], [sc-bytes], [cs-bytes], [time-taken] INTO IISLog FROM d:\logs\ex*.log WHERE TO_LOWERCASE (EXTRACT_EXTENSION(cs-uri-stem)) NOT IN (‘gif’;’jpg’;’png’;’bmp’;’ico’;’axd’)" -o:SQL -server:MIKEL\MIKEL -database:Logs -driver:"SQL Server" -username:sa -password:justdoit -createTable:OFF

Dos下通过Ping反查IP指向的域名命令如下:
ping -a 123.125.66.134

百度蜘蛛标示如下:

百度各个产品使用不同的user-agent:

产品名称 对应user-agent
无线搜索 Baiduspider-mobile
图片搜索 Baiduspider-image
视频搜索 Baiduspider-video
新闻搜索 Baiduspider-news
百度搜藏 Baiduspider-favo
百度联盟 Baiduspider-cpro
网页以及其他搜索 Baiduspider

1.视频\图片信息蜘蛛抓取IP段:61.135.168.44-61.135.168.50

2.js脚本抓取蜘蛛IP段:61.135.165.202-61.135.165.206

[转载]解决下载/上传大文件问题(IIS6)

mikel阅读(1064)

[转载]解决下载/上传大文件问题(IIS6).

Windows 2003,当在IIS6上下载(/上传)大文件时,

会出现错误(log 文件中):80004005 Response_Buffer_Limit_Exceeded。

原因:IIS6 有一个缺省的设置:AspBufferingLimit= 4194304 是4M。

解决办法:将AspBufferingLimit的值增大到合适的大小。

步骤:
1. 修改IIS设置,允许直接编辑配置数据库

管理工具->IIS管理器里,选择计算机,右键,选择属性,然后选中“Enable Direct Metabase Edit”.
2. 修改IIS配置文件

1). 先在服务管理器里关闭iis admin service服务

管理工具->服务->iis admin service->停止。

2). 用文本编辑器打开C:\Windows\System32\Inesrv\下的Metabase.xml文件 注意:修改文件之前请先      备份。

3). 修改对下载文件大小限制
找到AspBufferingLimit,把它修改为你所需的大小。
例如:AspBufferingLimit=”8388608″ (8M)
修改对上传文件大小限制
找到ASPMaxRequestEntityAllowed 把他修改为需要的值,默认为204800,即200K 把它修改为你所       需的大小。如:8388608(8M)
例如:AspRequestQueueMax=”8388608″

4). 然后开启www服务
管理工具->服务->World Wide Web Publishing Service->开启

备注:
也可以直接在控制台命令行运行 ADSUTIL.VBS更改此值。不用停止IIS。
例如改为8M:运行 “ADSUTIL.VBS SET W3SVC/AspBufferingLimit 8388608” 另外,可以在控制台命令行直接停止和启动IIS。

停止:“Net stop iisadmin /y”

启动:“Net start w3svc”

也可以使用IIS6 Resource Kit中的Metabase Explorer (MBExplorer.exe)(代替Metaedit 2.2)编辑。

IIS6 Resource Kit的下载地址:
http://support.microsoft.com/kb/840671
http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en

IIS6命令行工具的说明:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/html/5e7f8cde-4a01-42bd-acaf-f8f7d091ef7c.asp

[转载]升级到NVelocity1.1版本

mikel阅读(1121)

[转载]升级到NVelocity1.1版本 – 小草 – 博客园.

由于NVelocity项目太久没有升级了,虽然看到Velocity经常发布一些新的功能,但.net版本的修改似乎比较迟。以至于很少关注升级情况。由于前期使用这个模板引擎的时候发现对DataTable支持不太好,原先想自己修改一下源代码,但分析了一下源代码发现改不动,也没有精力去研究。(最 近越来越懒了^_^

今天看到Richie写的关于1.1的两篇文章发现新版本已经解决了我想要的 支持。真是太高兴了,随即结合《Castle NVelocity – 1.1整 理了一下相关的代码供大家参考。

原先使用0.4X版本的时候其实也可以使用DataTable,但就是有些麻烦,我也是经过反复的测试后终于想到一个办法来处理,作法如下:需要循环行记录的情 况下再循环列,然后根据列号把值写到变量里,相当的麻烦。

#foreach($Item in $dtSubSortList.Rows)

#set ($rownum = 0)

#foreach($value in $Item.ItemArray)

#set ($rownum = $rownum+1)

#if($rownum == 1)

#set($CATEGORY_ID = “$value”)

#end

#if($rownum == 2)

#set($CATEGORY_NAME = “$value”)

#end

#end

<tr> <td class=”daoh_1″><div class=”wenz_2″><a href=”$!{WebRoot}cms/listPage.aspx?categoryId=$!{CATEGORY_ID}&parent=$!{categoryid}”>$CATEGORY_NAME</a></div></td></tr>

#end

新版本的写法就非常的简单了:

#foreach($Item in $dtSubSortList.Rows)

<tr> <td class=”daoh_1″><div class=”wenz_2″><a href=”$!{WebRoot}cms/listPage.aspx?categoryId=$Item.CATEGORY_ID”>$Item.CATEGORY_NAME</a></div></td></tr>

#end

但现在不足的 之处就是还不支持索引的写法,如$Item[0] 或者 $Item[“字段名“]

我整理的部分代码里封装了 NVelocityHelper,并写一下模板页的基类PageBaseTemplate等内容,仅供大家参考。

PageBase.cs

这个属性可以通过一些扩展实现多级子站的模 板定制功能(大家可以自己思考一下,我只是使用了目录的方式进行处理,应该还有其它更好的方法)。

1.1版本的 使用说明在《Castle NVelocity – 1.1 》有详细的说明,非常感谢。

原代码及最新的组件都在这里: /Files/liubiqu/NVelocity1.1Demo.rar

代码内容:

由于时间关系还没有详细的研究,下列问题有待进一步测试:

1、 velocity.GetTemplate是否有提供一些缓存与优化的处理

2、 velocity.Evaluate的时候log的参数有什么用处及整 合到其它日志方法

3、在性能方 面有没有更好的处理方式。

[原创]ASP.NET MVC链接地址的中文信息编码

mikel阅读(742)


<%="/Systems/DealerSaleList?isNew=1&fromDate=" + this.dealerCondition.ShopName + "&finishDate=" + this.dealerCondition.Service+"&dealerName=" + System.Web.HttpUtility.UrlEncode(ds.DealerName, System.Text.Encoding.GetEncoding("UTF-8")) %>

[转载].NET基础 - 简单几句说说GC(垃圾回收器)

mikel阅读(883)

[转载].NET基础 – 简单几句说说GC(垃圾回收器) – 今天你开心么,朋友? – 博客园.

.NETFramework包含两大部分,其一为BCL(基础类库),其二为CLR(公共语言运行库),这里要说的正是CLR中的GCGarbage Collector),俗称垃圾 回收器。

我们不如先用正规的方法描述一下垃圾回收中的几个要点:

1 CLR创建对象时,发现CLR所控制的堆中的内存不足以创建该对象,触发GC进行垃圾收集。

2 等待当前所有.NET应用程序处于挂起状态下,才真正开始收集动作,并非马上执行,也没有办法立即执行!

3 为共享堆中所有被引用的对象建立白名单,同时将所有带有析构标志位的对象统一放置到FReachable红名单中(先要折磨一下再释放)。

4 开始释放,具体的形式呢就是把白名单中的对象统一移动到堆的底部,其他没有点到名的,就会被释 放掉啦,同时将堆指针更新到可用对象最高的那个位置。

5 现在来处理红名单中的哥几个,GC先执行这些对象中的析构函数,同时重置析构标志为已处理,然后等待他们的就是下一次的清理,在那之前这些哥们还都 在堆里。

下面再对上面的几个步骤做一下说明:

1 为对象创建析构函数的目的是为了释放掉非托管资源(即GC管不到的CLR外的,可以理解为OS资源),当然,我们也可以通过编写Dispose()方法来手动释放非托管资源,但不会释放掉堆中的资源。

2 不像C++中对析构函数的使用,.NET中应尽量少地使用析构函数,因为如果析构函数过多,我们必须至少调用两次GC回收,才能真正释放出资源。

3 我们可以通过编码为GC添加或减少内存压力,目的只有一个,就是借此来控制收集的时机。

4 充分考虑到GC收集的范围限制,在编写需要调用非托管资源的应用时,建议 提供Dispose()方法和析构函数两个手段,当然,我们可以通过使用公共代码的 方式,来保证两者的工作不会出现冲突。这样我们将获得灵活的处理能力。

5 同时,我们可以设定有关GC的一些操作特性,包括是否关闭多线程调用及是否对2代进行资源收集等等。

6 堆分为三代:012,其容量逐渐递增,但其活跃程度逐层递减,每次收集后,白名单中的哥们都会自动升级,很明显,级数越高,证明使用的时间 越长,越可能是全局的一些内容。

[转载]介绍一下我自己开发的全新Remoting技术。(本地调用远程代码)

mikel阅读(855)

[转载]介绍一下我自己开发的全新Remoting技术。(本地调用远程代码) – 美丽人生 – 博客园.

前言

——————

本文介绍了一种全新的调用远程代码的技术。参考了微软 的remoting、webservice。

基础知识

——————

先抛开具体的代码,如果要实现远程代码调用,一个最简 单的模型是:

1. 使用一个HttpHandler,当有请求的时候,调用对应的代码,返回。例如:

代码

class RemoteHandler : IHttpHandler, IReadOnlySessionState
{
public bool IsReusable
{
get
{
return true;
}
}

public void ProcessRequest(HttpContext context)
{
//这里创建实例RemotingGreeting

RemotingGreeting greeting
= new RemotingGreeting();


byte[] response = greeting.Helloworld();

//这里返回调用结果到客户端

context.Response.Clear();

context.Response.ContentType = application/octet-stream;

BinaryWriter writer = new BinaryWriter(context.Response.OutputStream);

writer.Write(response);

writer.Flush();

writer.Close();

context.Response.End();
}
}

2. 客户端使用Http去访问这个Handler,就实现了最原始的远程调用

这段代码,就实现了远程调用 RemotingGreeting这个类,获取方法Helloworld();的返回值。

那么,这个过程如何实现通用呢?如何实现框架化?首先 先看看实际代码的调用效果:

代码实例

——————

首先声明一个被远程调用的对象,RemotingGreeting. 以及一个接口IRemotingGreeing

代码

class RemotingGreeting : IRemotingGreeting
{
public string Greeting(string message)
{
return Hi! + message;
}
}

[Remote(Pixysoft.Framework.Remoting.Demo, Pixysoft.Framework.Remoting.Demo.RemotingGreeting)]//这里实际指定了接口具体实现的类的Assembly和Type
public interface IRemotingGreeting
{
string Greeting(string message);
}

然后本地实现远程调用:

代码

using System;
using System.Collections.Generic;
using System.Text;

namespace Pixysoft.Framework.Remoting.Demo
{
class testcase
{
public void test()
{
//指定了调用的入口点 url
string url = http://localhost:1300/Apis/remoting.asmx;

//创建本地调用的透明代理
IRemoteChannel<IRemotingGreeting> channel = RemotingManager.CreateRemoteChannel<IRemotingGreeting>(url);

//登录远程服务器
channel.Login(xxxxxx, xxxxxxxxx);

//远程调用
string greeting = channel.RemoteProxy.Greeting(pixysoft);

//登出
channel.Logout();

//打印结果,就是“Hi!pixysoft”
Console.WriteLine(greeting);
}
}
}

正文

——————

远程调用框架的思路是:

1. 本地创建一个透明代理(RealProxy.GetTransparentProxy())

2. 用户本地的请求,被透明代理序列化为XML

3. XML传递到服务器的Handler,被解析后,加载对应的对象(Spring? 动态加载)

4. Handler运行对象,获取返回值,再序列化为XML,返回本地。

5. 本地透明代理解析XML,获取返回值。

第一步,创建透明代理。请各位先阅读 一篇相关的文章:

http://www.cnblogs.com/zc22/archive/2010/02/22/1671557.html

这里贴出核心代码的一个例子:

代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;

namespace Pixysoft.Framework.TestDrivens
{
public class Mock<TInterface> : RealProxy
{
public Mock()
:
base(typeof(TInterface))
{
}

public TInterface Value
{
get
{
return (TInterface)this.GetTransparentProxy();
}
}

public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall
= msg as IMethodCallMessage;

//我返回 int = 1

return new ReturnMessage(1, null, 0, null, methodCall);
}
}

public interface IMock
{
int Devide(int a, int b);
}

public class testrealproxy //测试代码在这 里!!!
{
public void test()
{
IMock mock
= new Mock<IMock>().Value;

Console.WriteLine(mock.Devide(1, 2));

//输出 = 1
}
}
}

这篇文章讲解了如何实现一个接口的透明代理。本质在

public override IMessage Invoke(IMessage msg)

这里,对用户调用的方法进行序列化操作。

第二步,调用的序列化。

上文透明代理通过以下代码获取了用户调用的方法反射

IMethodCallMessage methodCall = msg as IMethodCallMessage;

MethodInfo method = methodCall.MethodBase as MethodInfo;

这里,要对调用方法MethodInfo进行序列化。 当然,就是自己去建立一个MethodInfo的xml描述,例如:

代码

<method assembly=”Pixysoft.Framework.Remoting” type=”Pixysoft.Framework.Remoting.Core.RemotingHelloworld” method=”HelloWorld” parametercount=”4″>
<parameter type=”DateTime” parameter=”para1″>2010-4-12 下午 08:52:21</parameter>
<parameter type=”String” parameter=”para2″>2</parameter>
<parameter type=”Int32″ parameter=”para3″>12</parameter>
<parameter type=”IRemotingValue” parameter=”para4″ />
<return type=”IRemotingValue” />
</method>

这个是我实际建立的MethodInfo的xml描 述。如何建立就不说了吧,很简单,用StringBuilder去拼就行了。

第三步,httpHandler解析 XML,加载对象运行结果。

客户端通过HttpPost到服务端,服务端获取了 XML之后,只要根据对应的参数加载Assembly,然后获取对象即可。具体涉及到了一些反射的操作:

Assembly assembly = Assembly.LoadFrom(assemblyname);

Type type = assembly.GetType(typename);

MethodInfo method = type.GetMethod(methodname);

获取了MethodInfo之后,只要把参数放入,获 取返回值即可。

代码

//实例化一个对象

ConstructorInfo constructorInfo
=
type.GetConstructor(BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.Instance,
null, new Type[] { }, null);

object remoteObject = constructorInfo.Invoke(new object[] { });

//调用这个对象的方法 这里省略了如何获取parameters过程

object returnvalue = method.Invoke(remoteObject, parameters);

第四步 Handler序列化返回值 为XML,返回本地。

只要把returnvalue序列化为xml即可。具 体就不叙述了。

第五步 本地透明代理解析XML,获取返回值。

本地透明代理把序列化的returnvalue再反序 列化为对象即可,然后返回

return new ReturnMessage(returnvalue, null, 0, null, methodCall);

难点讲解

——————

1. 整个调用过程最难的地方在于序列化操作。因为微软不支持接口的序列化、不支持内部类的序列化。这里需要自己实现。

2. 其次最难的在于值类型的操作。因为值类型进入了RealProxy之后,全部被装箱成为了对象(object)。这个时候直接把对象返回会抛异常,因此需 要根据具体的method.ReturnType, 逐一用值类型解析返回。

3. 再次,就是动态加载问题。Assembly.LoadFrom会有很多问题,比如版本问题、路径问题。因此要实现一个事件

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

实现了这个event之后,能够代码指定搜索 assembly的位置。具体代码我就不列举了。

后记

——————

写代码的过程,和拼装模型是一样的。只要我们手上的零 件越来越多,能实现的功能和效果就越来越多!

[转载]Web打印的解决方案之普通报表打印

mikel阅读(1079)

[转载]Web打印的解决方案之普通报表打印 – wuhuacong(伍华聪)的专栏 – 博客园.

做过很多的Web项目,大多数在打印页面内容的时候,采用的都是通过JavaScript调用系统内置的打印方法进行打印,也就是调用 PrintControl.ExecWB(?,?)实现直接打印和打印预览功能。打印的效果及控制性虽然不是很好,但是也能勉强使用,应付一般的打印还是 可以的了。

代码如下所示:

代码

//调用PrintControl.ExecWB(?,?)实现直接打印和打印预览功能。(直接用系统提供的print()方法打印无法隐藏某些 区域)
//
preview:是否显示预览。null/false:不显示,true:显示
function printPage(preview)
{
try
{
var content=window.document.body.innerHTML;
var oricontent=content;
while(content.indexOf({$printhide})>=0) content=content.replace({$printhide},style=’display:none’);
if(content.indexOf(ID=\PrintControl\“”)<0) content=content+<OBJECT ID=\PrintControl\ WIDTH=0 HEIGHT=0 CLASSID=\CLSID:8856F961340A11D0A96B00C04FD705A2\></OBJECT>;
window.document.body.innerHTML
=content;
//PrintControl.ExecWB(7,1)打印预览,(1,1)打开,(4,1)另存为,(17,1)全选,(10,1)属性, (6,1)打印,(6,6)直接打印,(8,1)页面设置
if(preview==null||preview==false) PrintControl.ExecWB(6,1);
else PrintControl.ExecWB(7,1); //OLECMDID_PRINT=7; OLECMDEXECOPT_DONTPROMPTUSER=6/OLECMDEXECOPT_PROMPTUSER=1
window.document.body.innerHTML=oricontent;
}
catch(ex){ alert(执行JavaScript脚本出错。); }
}

function printConten(preview, html)
{
try
{
var content=html;
var oricontent=window.document.body.innerHTML;
while(content.indexOf({$printhide})>=0) content=content.replace({$printhide},style=’display:none’);
if(content.indexOf(ID=\PrintControl\“”)<0) content=content+<OBJECT ID=\PrintControl\ WIDTH=0 HEIGHT=0 CLASSID=\CLSID:8856F961340A11D0A96B00C04FD705A2\></OBJECT>;

window.document.body.innerHTML=content;
//PrintControl.ExecWB(7,1)打印预览,(1,1)打开,(4,1)另存为,(17,1)全选,(10,1)属性, (6,1)打印,(6,6)直接打印,(8,1)页面设置
if(preview==null||preview==false) PrintControl.ExecWB(6,1);
else PrintControl.ExecWB(7,1); //OLECMDID_PRINT=7; OLECMDEXECOPT_DONTPROMPTUSER=6/OLECMDEXECOPT_PROMPTUSER=1
window.document.body.innerHTML=oricontent;
}
catch(ex){ alert(执行Javascript脚本出错。); }
}

上面两个函数放在一个Js文件中,在页面内容中通过应用该脚本文件并调用进一步封装的函数即可打印指定部分的内容:

<script language=javascript>
function Print(preview) {
var text = document.getElementById(content).innerHTML;
printConten(preview, text);
}

打印的效果大致如下图所示,如果打印的页面在框架页面中,那么需要选定“仅打印选定框架”的选项。

采用此种方法,不需要安装任何控件,具有很好的兼容优势,不过出来的报表内容,好像控制起来会比较麻烦一些,特别对于一些报表方面的打印,需要输出 复杂的内容是,也有一定的缺陷,但总体来说,也是一个较好的选择。

后来在需要做一些证件套打方面的工作,这个控件就做不到了,因此需要一种方法或者控件,能够较好处理套打方面的事情。

无意间,发现一个比较好的打印控件,支持各种格式的打印,还有我关心的证件套打功能,功能强大,使用也很简单的,非常值得推荐。

控件的相关地址:

控件下载主页:http://mt.runon.cn/index.html

控件博客介绍:http://blog.sina.com.cn/s/articlelist_1340389911_0_1.html

应用这个控件,普通报表的打印效果如下所示:

上面两个报表的打印其实都差不多,都是打印部分的HTML内容,不过后者看起来要好一点,而且提供很完善的报表功能设置。

代码大致如下所示。

代码

<script language=”javascript”>
function Print(preview) {
var text = document.getElementById(content).innerHTML;
printConten(preview, text);
}
</script>

<script language=”javascript” src=”http://www.cnblogs.com/Scripts/CheckActivX.js”></script>
<object id=”LODOP” classid=”clsid:2105C259-1E0C-4534-8141-A753534CB4CA” width=0 height=0> </object>
<script language=”javascript”>
var LODOP = document.getElementById(LODOP); //这行 语句是为了符合DTD规范
CheckLodop();
</script>
<script language=”javascript” type=”text/javascript”>

function Preview() {//打印 预览
CreateLicenseData();
LODOP.SET_SHOW_MODE(
PREVIEW_IN_BROWSE, 1);
LODOP.PREVIEW();
};
function Setup() {//打印 维护 给用户调整位置
CreateLicenseData();
LODOP.PRINT_SETUP();
};
function Design() {//打印 设计 开发人员设置内容和位置
CreateLicenseData();
LODOP.PRINT_DESIGN();
};

function CreateLicenseData() {
LODOP.PRINT_INIT(
查询报表);
LODOP.ADD_PRINT_HTM(
20, 40, 610, 900, document.all(content).innerHTML);
LODOP.PREVIEW();
}
</script>

很多时候,我们也没的内容,都是通过CSS来控制美观的,所以有时候,我们打印部分HTML,没有这些样式的话,那么出来的Table格式和字体, 可能都会发生变化,不太好看。那么就需要进行HTML的样式设置。

如果给打印内容设置了样式,那么出来的界面效果就好很多了。

设置样式的代码如下所示。

代码

<script language=javascript type=text/javascript>

function Preview() {//打印预览
CreateLicenseData();
LODOP.SET_SHOW_MODE(
PREVIEW_IN_BROWSE, 1);
LODOP.PREVIEW();
};

function CreateLicenseData() {
LODOP.PRINT_INIT(申请处理单);
var strBodyStyle
= <link type=’text/css’ rel=’stylesheet’ href=’http://www.cnblogs.com/Themes/Default/style.css’ /><style><!–table { border:1;background-color: #CBCBCC } td {background-color:#FFFFFE;border: 1; } th { background-color:#F1F1F3;padding-left:5px;border:1}–></style>;
var strFormHtml
= strBodyStyle + <body> + document.getElementById(content).innerHTML + </body>;
LODOP.ADD_PRINT_HTM(
20, 40, 610, 900, strFormHtml);
LODOP.PREVIEW();
}
</script>

下一篇继续介绍下证件套打的打印功能。

主要研究技术:代码生成工具、Visio二次开发

转载请注明出处:
撰写人:伍华聪  http:
//www.iqidi.com

[转载]Web打印的解决方案之证件套打

mikel阅读(1021)

[转载]Web打印的解决方案之证件套打 – wuhuacong(伍华聪)的专栏 – 博客园.

由于以前未接触过套打,一直觉得套打是一个比较神秘和麻烦的事情,因为打印机的位置总是需要调整的,你总不能硬编码吧?但是如果位置可调,有需要直 观一些来处理,那就比较麻烦了。

在前面介绍过《Web打印的解决方案之普通报表打印》的一片文章中提到过那个打印控件Lodop,做起套打来感 觉还是挺方便的,至少位置调整界面不需要自己弄,位置嘛,也提供了自动保存的功能,不需要理会。

一般的套打,包含了几部分操作:打印预览、打印维护、打印设计。

打印预览和打印维护是面向终端用户的,打印维护是指内容不能修改删除、但位置可以调整,给不同的打印机不同的尺寸打印提供调整位置的可能性。

打印设计是面向开发人员的,开始需要通过这个功能来设计好套打的界面,就是根据套打证件的背景图片,大致摆放好各个内容的位置。

大致的实现代码如下所示:

<script language=”JavaScript>
var LODOP=document.getElementById(LODOP);//这行 语句是为了符合DTD规范
CheckLodop();
</script>
<script language=”JavaScript type=”text/JavaScript>

function Preview2() {
CreateDataBill();
LODOP.PREVIEW();
};
function Setup2() {
CreateDataBill();
LODOP.PRINT_SETUP();
};
function Design2() {
CreateDataBill();
LODOP.PRINT_DESIGN();

};
function RealPrint() {
CreateDataBill();
if (LODOP.PRINTA())
alert(
已发出实 际打印命令!);
else
alert(
放弃打 印!);
};

function CreateDataBill() {
LODOP.SET_PRINT_PAPER(
10,10,762,533,打印控件 功能演示_Lodop功能_移动公司发票套打);
LODOP.ADD_PRINT_TEXT(
126,150,100,20,郭德刚);
LODOP.SET_PRINT_STYLEA(
1,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
151,150,100,20,13954885177);
LODOP.SET_PRINT_STYLEA(
2,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
125,584,99,20,发票打印 (第1次));
LODOP.SET_PRINT_STYLEA(
3,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
465,140,198,20,陆百柒拾 捌元叁角零分);
LODOP.SET_PRINT_STYLEA(
4,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
465,599,70,20,678.30);
LODOP.SET_PRINT_STYLEA(
5,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
496,408,59,20,H112063);
LODOP.SET_PRINT_STYLEA(
6,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
191,58,100,20,国内漫游 通话);
LODOP.SET_PRINT_STYLEA(
7,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
191,217,100,20,584.00);
LODOP.SET_PRINT_STYLEA(
8,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
222,58,100,20,增值业务 费);
LODOP.SET_PRINT_STYLEA(
9,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
222,217,100,20,48.30);
LODOP.SET_PRINT_STYLEA(
10,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
251,58,100,20,代收费);
LODOP.SET_PRINT_STYLEA(
11,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
251,217,100,20,50.00);
LODOP.SET_PRINT_STYLEA(
12,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
280,58,100,20,优惠费);
LODOP.SET_PRINT_STYLEA(
13,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
280,217,100,20,4.00);
LODOP.SET_PRINT_STYLEA(
14,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
98,101,150,20,101081005747319387);
LODOP.SET_PRINT_STYLEA(
15,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
97,307,150,20,2008 年10月19日 10:28:38);
LODOP.SET_PRINT_STYLEA(
16,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
152,584,103,20,138860016786);
LODOP.SET_PRINT_STYLEA(
17,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
95,571,112,20,06775516);
LODOP.SET_PRINT_STYLEA(
18,FontName,System);
LODOP.SET_PRINT_STYLEA(
18,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
496,135,183,20,2008 年09月(20080901-20080930));
LODOP.SET_PRINT_STYLEA(
19,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
496,572,112,20,-王府井 中心店营);
LODOP.SET_PRINT_STYLEA(
20,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
311,217,100,20,678.30);
LODOP.SET_PRINT_STYLEA(
21,FontColor,16711680);
LODOP.ADD_PRINT_TEXT(
311,58,100,20,费用合计);
LODOP.SET_PRINT_STYLEA(
22,FontColor,16711680);

LODOP.ADD_PRINT_SETUP_BKIMG(<img src=Report/ 证件背景.jpg’ />);
LODOP.SET_SHOW_MODE (
BKIMG_IN_PREVIEW,1);
}
</script>

其中大部分内容还是比较好理解的,不同的功能调用不同的函数Preview为预览、Setup为维护、Design为设计。

在套打的时候,注意需要通过下面代码来设置显示背景图片(打印的时候,是不打印背景的)。

LODOP.ADD_PRINT_SETUP_BKIMG(“<img src=Report/证件背景.jpg’ />“);
LODOP.SET_SHOW_MODE (“BKIMG_IN_PREVIEW”,1); //打印预览时是否包含背景图

另外,由于报表的内容都是写在HTML页面中的,因此在动态设置内容的时候,一可以用脚本来读取界面元素作为数据源,二可以通过后台代码自动生成 脚本代码,输出到前台页面中,看具体的需要了。

套打的预览界面大致如下图所示,其中蓝色部分是需要打印的内容,背景图片是一个发票的样板。

下面代码是通过脚本获取界面元素来取得相应的内容的,这种情况适合于界面上可能会修改一些打印的内容的情景。

LODOP.ADD_PRINT_TEXT(95, 695, 250, 52, document.getElementById(“txtCompanyName”).innerText); // 业户名称
LODOP.SET_PRINT_STYLEA(1, “FontSize”, 14);
LODOP.SET_PRINT_STYLEA(1, “Bold”, 1);

var licenseDate = document.getElementById(“txtLicenseDate”).value; //证件有效期
var licenseYear = “”;
var licenseMonth = “”;
var licenseDay = “”;
if (licenseDate != null && licenseDate != “”) {
licenseYear = licenseDate.split(“-“)[0];
licenseMonth = licenseDate.split(“-“)[1];
licenseDay = (licenseDate.split(“-“)[2]).substr(0, 2);
}

LODOP.ADD_PRINT_TEXT(396, 190, 46, 22, validateYear); // 有效期 结束 年
LODOP.SET_PRINT_STYLEA(6, “FontSize”, 11);
LODOP.SET_PRINT_STYLEA(6, “Bold”, 1);
LODOP.ADD_PRINT_TEXT(396, 253, 30, 22, validateMonth); // 有效期 结束 月
LODOP.SET_PRINT_STYLEA(7, “FontSize”, 11);
LODOP.SET_PRINT_STYLEA(7, “Bold”, 1);
LODOP.ADD_PRINT_TEXT(396, 304, 32, 22, validateDay); // 有效期 结束 日
LODOP.SET_PRINT_STYLEA(8, “FontSize”, 11);
LODOP.SET_PRINT_STYLEA(8, “Bold”, 1);

最后附上它的设计界面,其中生成代码功能可以生成用于静态HTML中的内容布局显示,做一定的修改调整就可以用在动态页面中了。非常有用的一个功 能。

主要研究技术:代码生成工具、Visio二次开发

转载请注明出处:
撰写人:伍华聪  http:
//www.iqidi.com

[转载]也谈WEB打印(三):抛开IE,实现我们自己的打印模板

mikel阅读(996)

[转载]也谈WEB打印(三):抛开IE,实现我们自己的打印模板 – 悼念死难同胞 – 博客园.

在上一篇文章《也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览》中,我们剖析了IE的打印原理,并学会了如何调用IE的打印功能,在这篇文章中, 我们实现写自己的打印模板。

IE5.5开始,你可以定制WebBrowser控件和IE如何打印 和预览文档。打印和预览的机制有打印模板控制,这是一些HTML文件,他 们可以由开发人员可以使用由打印模板行为暴露的对象模型来生成。MSDN上 说,如果打印模板是HTML文件的时候,定制的打印模板只能由C++语言从发布或拦截IDM_PRINT/IDM_PRINTPREVIEW命令的程序或控件中使用(打印模板的路径由IOleCommandTarget::ExecpvaIn参数 指定)。但实际上,DelphiC#语言也是可以的,我已经做过测试了。至于VB可不可以,我没有试验过,不敢肯定。缺省情况下,IE使用自己的打印模板,就是说,如果IOleCommandTarget::ExecpvaIn参数 为NULL的时候,IE就会调用自己的打印模板进行打印。

我们可以用打印模板做一 些什么事情呢?
1、 控制打印和预览时页面的版面,打印以及预览的内容;
2、 控制打印任务如何被处理,例如以何种顺序打印页面;
3、 控制打印预览窗口的外观,在打印用户界面上放置定制的控 件。

有了打印模板,我们可以加入一些非常酷的功能,例如加入公司的Logo,法律声明,广告;定制页眉页脚的位置和样式;安排打印计划进行定时打印,如此等等。
一个打印模板其实就是一个标准的HTML文件,与其他的HTML文件不同的是,他有4个声明模板的基本元素:LAYOUTRECTDEVICERECTTEMPLATEPRINTERHEADERFOOTER。下面讲解一下这4个 模板元素的作用:
LAYOUTRECT:为打印和预览模板中的文档内容生成容器.
DEVICERECT:为LAYOUTRECT元素和打印模板中的其他内容提供一个容器。位于DEVICERECT之外的内容都不会被打印。
TEMPLATEPRINTER:提供一个打印模板以获取页 面设置和打印设置,并且可以控制从打印模板初始化出来的打印作业。
HEADERFOOTER:提供一个工具以便打印模板可以把 页眉和页脚的格式化字符串转换为格式化的HTML文本。
下面,我们先写一个最小的具有预览但是不能打印的模板(模板代码来自于MSDN,我只做了一点点说明):

1<!– Template1.htm: 最小的模板(只能预览)
2这个模板展示了一个最小的模板,他支持打印预览,但不能打印。它只包含2个DEVICERECT元素,每个元素有包含一个 LAYOUTRECT元素。在预览的时候,他并没有完全显示原文档,LAYOUTRECT元素的样式表定义了必须的宽度和高度属性。其他属性则不是必须 的。LAYOUTRECT和DEVICERECT元素包含颜色和边框属性(分别是白色和黄色)使得容易辨认他们边界。DEVICERECT元素尺寸是 8.5 x 11英寸,这并不是IE“页面设置”里的实际值。
3–>
4<HTML XMLNS:IE>
5<HEAD>
6<?IMPORT NAMESPACE=”IE” IMPLEMENTATION=”#default”>
7<STYLE TYPE=”text/css”>
8.lorstyle
9{
10 width:5.5in;
11 height:8in;
12 margin:1in;
13 background:white;
14 border:1 dashed gray;
15}
16.pagestyle
17{
18 width:8.5in;
19 height:11in;
20 background:#FFFF99;
21border-left:1 solid black;
22border-top:1 solid black;
23border-right:4 solid black;
24border-bottom:4 solid black;
25 margin:10px;
26}
27</STYLE>
28</HEAD>
29<BODY>
30<IE:DEVICERECT ID=”page1″ CLASS=”pagestyle” MEDIA=”print”>
31 <IE:LAYOUTRECT ID=”layoutrect1″ CONTENTSRC=”document” CLASS=”lorstyle” NEXTRECT=”layoutrect2″/>
32</IE:DEVICERECT>
33<IE:DEVICERECT ID=”page2″ CLASS=”pagestyle” MEDIA=”print”>
34 <IE:LAYOUTRECT ID=”layoutrect2″ CLASS=”lorstyle”/>
35</IE:DEVICERECT>
36</BODY>
37</HTML>
38

把上面的代码保存,然户运行我在上一篇文章中所是的代码,才Document Address框中输入www.cnblogs.com,然后单击Preview,其效果如下图:

上面的例子还很不完善,打印预览只会显示2个页面,不管你有多少个页面,他也只会显示前2页。而且,页面也是固定了的,不管你实际页面的大小是多少,或者你在IE的“页面设置”里设置的大小是多少,他都不会调整,只会按8 x 11.5英寸的页面大小显示。另外,还不支持打印,即使你按下“Print”按钮,他也不会打印,而只是执行预览的动作。

我们注意到,每个标签都以一个命名空间声明开始(IE:),一个打印模板必须在第一行一如下的形式声明他的命名空间:
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE=“IE” IMPLEMENTATION=“#default”>
在这里,HTML标签的XMLNS属 性为模板声明了一个叫做IE的命名空间,你也可以把“IE”替换成任何你喜欢的名字,当然,在后面的所有命名空间也必须相应的改成你喜欢的那个名字。IMPORT标签然后导入了Internet ExplorerIE命名空间的实 现,使得打印模板上的元素的行为对模板有效。

值得注意的是,LAYOUTRECT没有结束标签,在他的结束括号之前必须放一个反斜线,这类似于XML文档中没有结束标签的元素的语法一样。LAYOUTRECT中不能包含任何HTML,例如,如下的语句不会有效:
layoutrect1.innerHTML = “<B>Hi there!</B>”;
现在我们来看看属性。ID属性和CLASS属 性是大家所熟知的,值得注意的是,CLASS指定的样式中必须包含有WidthHeight。 在本例中,这2个值都不符合实际。当然,我们可以从TemplatePrinter 行为中取得正确的值。不过我们先不讨论这个。另外一个属性就是MEDIEA,大家都知道他的含义,不过,在这里,我们必须指定MEDIA属性的值是“print”,这是强制性的。另外就是CONTENTSRC,如果他的值 是“document”,则表示需要打印或预览的是IE当 前显示的页面,如果不想打印这个文档,那么,可以指定其他的URL,例如http://yahong111.cnblogs.com,这样的话,就会打印或预览http://yahong111.cnblogs.com的内容,而不是当前页面。最后一个是NEXTRECT,他表示如果当前页满了时候,下一个LAYOUTRECT的名字,在本例中,“layoutrect1”的NEXTRECT就 是“layoutrect2”,如果当前页是最后一页,那么就不要指定NEXTRECT的值。被打印或预览的文档有多少页,我们就必须有多少个LAYOUTRECT。在本例中,我们只指定了2LAYOUTRECT,所以,只会显示2页,而且,即使被打印文档只有一页,他也会显示2页。
下面是DEVICERECTLAYOUTRECT的图解。

那么,我们如何知道当前被打印或预览的文档需要多少个LAYOUTRECT呢?我们如何正确设置每个DEVICERECTLAYOUTRECTWidthHeight呢?这是我们下一篇文章所讨论的内容。
参考文档:《Beyond Print Preview: Print Customization for Internet Explorer 5.5

另,附上增强的DLPrinter.CAB组件,现在组件可以设置页眉、页脚和纸张的走向了(横向/纵向)。因不支持.cab文件上传,所以把DLPrinter.CAB,改成了DLPrinter.cab.rar,请大家把文件名改过来就可以用了。
使用方法如下:
<OBJECT ID=”DLPrinter” CLASSID=”CLSID:5C230622-45E5-4e3c-893C-3BFDDC4DB5E4″ height=”0″ width=”0″ codebase=”DLPrinter.cab” ></OBJECT>
<script>
DLPrinter.MarginLeft=20;
DLPrinter.PageHeader=”这是测试的页眉“;
DLPrinter.PageFooter=”这是测试的页脚“;
DLPrinter.IsLandScape=1; //把页面设置为横向
//DLPrinter.ContentURL=http://www.cnblogs.com/Yahong111/archive/2007/09/19/898326.html;
</script>
<input type=”button” id=”btnPrint” value=”Print Preview” onclick=”DLPrinter.PrintPreview()” />

欢迎大家拍砖!