建立Silverlight2.0控件(Usercontrol)

Silverlight 2.0有一个非常重要的部分,就是控件技术的出现,透过可自行定义开发的控件建立机制,Silverlight ASP.NET一样,可以自行定义可重用的 UserControl,这对于开发人员有着相当重要的意义,这表示对象化的设计、ReUse与大量的开发开始变的可能。

< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

有这些作为基础,我们才有可能真正透过Sivlerlight 2.0建立中大型的应用程序或是RIA,也才有可能确保投资可以延续并且在团队开发当中累积资源。

这么重要的机制,当然要给他试用一下,上图是我尝试建立的traffic light控件,模拟一个红绿灯,一样具有属性、事件、方法……可以跟外部的程序互动,画面中的ButtonTextBox则是Silverlight 2.0当中本来就有的控件,通过.NET Code的开发方式,你可以用C#或是VB,确实比过去1.0时代用JavaScriptAJAX方便很多。

在该控件在被使用者点选之后,会触发一个Click事件,这个事件是我们自己建立出来的,我们可以在这个事件当中改变TextBox内的值(你试用看看就知道)。当然,我们也可以在其他控件(例如按钮)当中来改变灯号(通过traffic light控件的属性),总而言之,这就是一个标准的控件啦。

您可以点选[这里]测试一下这个控件,关于程序代码的说明我会在七月的Run!PC当中介绍,如果您需要Source Code,请私下偷偷Mail给我。

.NETPetshop详解(三):petshop三层结构之MiddleTire

通过前面的文章,我们对.NET Petshop的整个结构有了一个大致的了解,也清楚的知道了数据库的设计模式和实现的细节,尤其值得一提的是通过存储过程访问数据库。在接下来的这篇文章里,我将和大家一起来探究一下.NET Petshop的中间层。

根据三层结构的设计原则,中间层封装的是业务逻辑和规则,在这个网络宠物商店的例子中,购物处理,订单处理,帐号管理,产品查询等等都是具体的业务逻辑,至于与用户交互并不是中间层要处理的问题。它处理是与具体的用户界面和交互无关,而仅仅是核心的商业规则和逻辑。.NET Petshop的中间层业务逻辑被封装为一个.NET 组件,它的命名空间为Pet Shop.Components(编译后在bin的文件夹里面有一个petshop.dll的文件)。图1.NET Petshop解决方案中间层的类视图和文件视图。

< xml:namespace prefix = v ns = “urn:schemas-microsoft-com:vml” /><shapetype id=”_x0000_t75″ coordsize=”21600,21600″ o:spt=”75″ o:preferrelative=”t” path=”m@4@5l@4@11@9@11@9@5xe” filled=”f” stroked=”f”><stroke joinstyle=”miter”></stroke><formulas><f eqn=”if lineDrawn pixelLineWidth 0″></f><f eqn=”sum @0 1 0″></f><f eqn=”sum 0 0 @1″></f><f eqn=”prod @2 1 2″></f><f eqn=”prod @3 21600 pixelWidth”></f><f eqn=”prod @3 21600 pixelHeight”></f><f eqn=”sum @0 0 1″></f><f eqn=”prod @6 1 2″></f><f eqn=”prod @7 21600 pixelWidth”></f><f eqn=”sum @8 21600 0″></f><f eqn=”prod @7 21600 pixelHeight”></f><f eqn=”sum @10 21600 0″></f></formulas><path o:extrusionok=”f” gradientshapeok=”t” o:connecttype=”rect”></path>< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” /><lock v:ext=”edit” aspectratio=”t”></lock></shapetype>

1.NET Petshop解决方案中间层的类视图和文件视图

接下来,我们模拟顾客到百货超市采购日常用品的过程来说明运作的流程以及抽象出重要的概念(实际上User case,我们在领域分析的时候会这么做,并且是很重要的一步,从这里可以初步的发现在我们实施的系统中将要涉及到的逻辑实体,进而可以为数据库建模设计以及类设计提供参考)。

购物用例的业务分析:

1 客户有购买商品的意愿;

2 客户到登陆管理员处登记,且成功登记;

3 在登记处推一个购物车;

4 在超市内查找所购商品类别存放的货架;

5 在具体的货架上查找某一具体品牌的商品;

6 将符合意愿的商品放入自己的购物车;

7 重复4-6

8 购物完毕;

9 到付款处计算总价格并付款;

10 打印购物清单;

11 退还购物车;

12 取走购物,购物完毕;

备注:在这个用例中,我们做了一些前提和假设,为的是方便.NET Petshop的分析,比如说在实际生活中根本就不需要第二步。

通过这个用例的分析,我们至少可以抽象出一下几个重要概念,并且能在应用程序里面找到对应的类:客户对应Customer、商品对应Product、购物车对应ShoppingCart、商品类别对应Category、具体商品对应Item、清单对应Order

正如我前面说过的,这几个概念对于我们的业务建模和系统建模是非常有用的。正是通过这样的分析,在.NET Petshop的业务逻辑里面共有9个核心类和5个轻量级的数据结构类。同样的方式,我在这里列出这些类,并加以说明(见表1)。

类名称

说明

BasketItem

代表购物车ShoppingCart里的一项购物商品。

Customer

用于帐号管理和登陆验证。

CustomerDetails

用户帐号的详细信息。

CustomerAddress

用户帐号的地址信息。

Error

用于登陆出错的帮助功能。

Item

代表某类产品中的具体一项商品。

ItemResults

搜索Item的结果集。

Order

购物完毕后的购物清单和订单。

Product

大类别里面的某类产品。

ProductResults

搜索产品的结果集。

Profile

用户的配置。

ShoppingCart

购物车,用于购物的整个过程,直到下订单。

Database

通过ADO.NET访问数据库,封装了具体的访问方法。

SearchResults

模糊搜索的结果集。

1.NET Petshop中间层的类

CustomerAddress, CustomerDetails, ItemResults, ProductResults, and SearchResults这几个轻量级的数据结构类为在数据层和展示层之间提供了一种松散的数据绑定调用。这些类都被设计为有公开的属性,ASP.NET web页面可以通过这些属性访问数据。下面这段类的代码说明了这5个类是如何暴露自己的公开属性供展示层使用的。

public class ProductResults

{

private string m_productid;

private string m_name;

public string productid {

get { return m_productid; }

set { m_productid = value; }

}

public string name {

get { return m_name; }

set { m_name = value; }

}

}

.NET Petshop详解(二)中我们就说过数据库的访问是通过存储来进行的,我们看看下面这部分代码就知道了:

public string Login(string userName, string password) {

string customerID;

// params to stored proc

Database data = new Database();

SqlParameter[] prams = {

data.MakeInParam(“@username”, SqlDbType.VarChar, 25, password),

data.MakeInParam(“@password”, SqlDbType.VarChar, 25, userName),

data.MakeOutParam(“@CustomerID”, SqlDbType.VarChar, 25)

};

// create data object and params

data.RunProc(“upAccountLogin”, prams); // run the stored procedure

customerID = (string) prams[2].Value; // get the output param value

// if the customer id is an empty string, then the login failed

if (customerID == string.Empty)

return null;

else

return customerID;

}

这段代码是Customer类的Login方法,它是通过将用户输入的用户名userName和密码password做为输入参数传递给存储过程upAccountLogin的,这个存储过程完成在Sigon用户帐号表里面查找该用户是否合法,最后返回一个字符串的用户ID值。在这里没有使用SQL查询语句,很好的分离了逻辑。具体的数据库访问是通过Database来完成的,我们将在后面的文章中继续探讨它的运作。

ShoppingCart是比较有意思的一个类,也是很重要的一个类。它是与状态有关的一个类,在.NET Petshop里面,它的状态是通过ASP.NET Session state来管理的,关于其进一步的细节留待后面讨论。

.NET Petshop的中间层的探讨先到此,我在这里只是抛砖引玉,很多的东西要深入代码才可以搞的更加清楚。欢迎大家继续与我一起关注下一篇.NET Petshop的数据展示层。

如何快速实现HTML编辑器.NET组件

作者:未知 请作者速与本人联系

得到“素材”

首先我们需要得到一个HTML编辑器的原始代码,网上有不少这类的编辑器,如大名鼎鼎的RichTextBox,为了避免版权纠纷,以我所做得为例(暂名:UltraTextBox):在编辑器工具栏的空白地方点击鼠标右键-->查看源代码,如图所示。
把代码拷贝出来保存成一个.htm文件就可以看到效果,是不是感觉很简单的就作了一半?:)
为了以后讲解方面我们把它保存为editor.aspx文件,在这里注意删除掉__VIEWSTATE一段。
然后把相应的图标,CSS文件等保存在相应的位置,否则你的界面会很难看,当然你也可以根据需要自己来做图标。
好了,准备工作基本做完,下面是讲怎样把它封装为.NET组件,方便你在工程中使用。
——————————————————————————–
封装成ASP.NET组件
首先在VS.NET环境里生成一个UltraTextBoxV1组件(也可以称为自定义控件,我习惯称为组件)项目,
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
//设置该组件的标记前缀
[assembly:TagPrefix("gOODiDEA.UltraTextBoxV1", "UTBV1")]
namespace gOODiDEA.UltraTextBoxV1
{
    //添加类声明 
    [
    DefaultProperty("Text"),
    ValidationProperty("Text"),
    ToolboxData("<{0}:UltraTextBoxV1 runat=server></{0}:UltraTextBoxV1>"),
    ParseChildren(false),
    Designer("gOODiDEA.UltraTextBoxV1.UltraTextBoxV1Designer")
    ]
    public class UltraTextBoxV1: System.Web.UI.Control, IPostBackDataHandler
    {
        private static readonly object ValueChangedEvent = new object();
        //声明一个代理用于处理值被改变的事件,当组件的值更改时发生ValueChanged事件 
        public event EventHandler ValueChanged
        {
            add
            {
                Events.AddHandler(ValueChangedEvent, value);
            }
            remove
            {
                Events.RemoveHandler(ValueChangedEvent, value) ;
            }
        }
        //触发值被改变事件的方法
        protected virtual void OnValueChanged(EventArgs e)
        {
            if( Events != null )
            {
                EventHandler oEventHandler = ( EventHandler )Events[ValueChangedEvent] ;
                if (oEventHandler != null) oEventHandler(this, e);
            }
        }
        //处理回发数据 
        bool IPostBackDataHandler.LoadPostData( string postDataKey, 
System.Collections.Specialized.NameValueCollection postCollection ) { if ( postCollection[postDataKey] != Text ) { Text = postCollection[postDataKey]; return true; } return false; } //告诉应用程序该组件的状态已更改 void IPostBackDataHandler.RaisePostDataChangedEvent() { OnValueChanged( EventArgs.Empty ); } //我们对一个编辑器主要需要实现下面4个属性,Text,Width,Height,BasePath。 //Text属性:(从编辑器得到值和把值赋给编辑器) [Bindable(true),DefaultValue("")] public string Text { get { object o = ViewState["Text"]; return ( o == null )   String.Empty : ( string )o; } set { ViewState["Text"] = value; } } //Width属性:(编辑器的宽) [Bindable(true),Category("Appearence"),DefaultValue("100%")] public Unit Width { get { object o = ViewState["Width"]; return ( o == null )   Unit.Parse( "100%" ) : ( Unit )o ; } set { ViewState["Width"] = value ; } } //Height属性:(编辑器的高) [Bindable(true),Category("Appearence"),DefaultValue("385px")] public Unit Height { get { object o = ViewState["Height"]; return ( o == null )   Unit.Parse( "385px" ) : ( Unit )o ; } set { ViewState["Height"] = value ; } } //BasePath属性:(第一步保存的editor.aspx的路径以及以后做的插件的路径) [Bindable(true),DefaultValue("../UltraTextBoxV1Sys/Plug-Ins/")] public string BasePath { get { object o = ViewState["BasePath"]; return (o == null)   "../UltraTextBoxV1Sys/Plug-Ins/" : (string)o; } set { ViewState["BasePath"] = value; } } //接下来是最重要的怎样把本组件和Editor.aspx结合起来,这里使用的是iframe技术: //覆盖Render方法,运行时输出: protected override void Render(HtmlTextWriter output) { System.Web.HttpBrowserCapabilities oBrowser = Page.Request.Browser ; //对应的IE版本必须是5.5或以上的版本 if (oBrowser.Browser == "IE" && oBrowser.MajorVersion >= 5.5 && oBrowser.Win32) { string sLink = BasePath + "Editor.aspx FieldName=" + UniqueID; //如果不使用SetTimeout则会提示找不到对象 output.Write( "<IFRAME id=\"{5}\" src=\"{0}\" width=\"{1}\" height=\"{2}\" frameborder=\"no\" scrolling=\"no\"
onload=\"javascipt:setTimeout('{5}.HtmlEdit.document.body.innerHTML = document.getElementById(\\'{4} \\').value',1000);\" onblur=\"{4}.value = {5}.HtmlEdit.document.body.innerHTML\"></IFRAME>", sLink, Width, Height, Text, UniqueID, ID + "_editor" ) ; //存储编辑器的值 output.Write( "<INPUT type=\"hidden\" id=\"{0}\" name=\"{0}\" value=\"{1}\" >", UniqueID, System.Web.HttpUtility.HtmlEncode(Text) ) ; } } } //接下来给该组件实现一个设计时的界面: public class UltraTextBoxV1Designer : System.Web.UI.Design.ControlDesigner { public UltraTextBoxV1Designer(){} public override string GetDesignTimeHtml() { UltraTextBoxV1 oControl = ( UltraTextBoxV1 )Component ; return String.Format( "<TABLE width=\"{0}\" height=\"{1}\" bgcolor=\"#f5f5f5\" bordercolor=\"#c7c7c7\" cellpadding=\"0\"
cellspacing=\"0\" border=\"1\"><TR><TD valign=\"middle\" align=\"center\">UltraTextBox 1.1 - <B>{2}</B></TD></TR></TABLE>", oControl.Width, oControl.Height, oControl.ID ) ; } } }
至此组件部分就基本做完,把它编译后的Dll拷贝你的项目文件夹下,在工具栏-->组件里添加它,你就可以直接拖放进你的页面,在你的工程中使用。
——————————————————————————–
添加插件
这里举两个例子来说明怎样给该编辑器添加插件:
如果你要给编辑器添加一些功能,如上传图片,插入标签等,则首先应该给它添加一个图标:
<div class="Btn" TITLE="上传图片" LANGUAGE="javascript" onclick="UTB_InsertImg()">
<img class="Ico" src="..\images\img.gif" WIDTH="16" HEIGHT="16">
</div>

<div class="Btn" TITLE="插入EXCEL表格" LANGUAGE="javascript" onclick="UTB_InsertExcel()">
<img class="Ico" src="..\images\insertexcel.gif" WIDTH="16" HEIGHT="16">
</div>
然后在JScript代码里添加UTB_InsertImg(),UTB_InsertExcel()的实现:
function UTB_InsertImg()
{
//只能在编辑模式下使用
if ( ! UTB_validateMode() )
return;
HtmlEdit.focus();
//在当前光标处创建一个区域用于插入图片
var range = HtmlEdit.document.selection.createRange();
//用模式对话框打开上传页面,把返回值插入到编辑器中
var arr = showModalDialog(""uploadface.aspx"", """", ""dialogWidth:430px;dialogHeight:280px;help:0;status:0"");
if (arr != null)
{
//得到的返回值应该是形如:<img src="http://61.139.77.178:8088/gOODiDEA/pic01.jpg">
range.pasteHTML( arr );
}
HtmlEdit.focus();
}

function UTB_InsertExcel()
{
if (!UTB_validateMode())
return;
HtmlEdit.focus();
//在这里其实就是插入一个Microsoft Office Web Components(MSOWC)组件
var range = HtmlEdit.document.selection.createRange();
range.pasteHTML(""<object classid='clsid:0002E510-0000-0000-C000-000000000046' id='Spreadsheet1' 
codebase='file:\\Bob\software\office2000\msowc.cab' width='100%' height='250'><param name='HTMLURL' value>
<param name='HTMLData' value='&lt;html xmlns:x=&quot;urn:schemas-microsoft-com:office:excel&quot;xmlns=&quot;
http://www.w3.org/TR/REC-html40&quot;&gt;&lt;head&gt;&lt;style type=&quot;text/css&quot;&gt;&lt;
!--tr{mso-height-source:auto;}td{black-space:nowrap;}.wc4590F88{black-space:nowrap;font-family:宋体;
mso-number-format:General;font-size:auto;font-weight:auto;font-style:auto;text-decoration:auto;
mso-background-source:auto;mso-pattern:auto;mso-color-source:auto;text-align:general;vertical-align:bottom;
border-top:none;border-left:none;border-right:none;border-bottom:none;mso-protection:locked;}--&gt;&lt;/style&gt;
&lt;/head&gt;&lt;body&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt;&lt;x:ExcelWorkbook&gt;&lt;x:ExcelWorksheets&gt;&lt;
x:ExcelWorksheet&gt;&lt;x:OWCVersion&gt;9.0.0.2710&lt;/x:OWCVersion&gt;&lt;x:Label Style='border-top:solid .5pt silver;
border-left:solid .5pt silver;border-right:solid .5pt silver;border-bottom:solid .5pt silver'&gt;&lt;x:Caption&gt;
Microsoft Office Spreadsheet&lt;/x:Caption&gt; &lt;/x:Label&gt;&lt;x:Name&gt;Sheet1&lt;/x:Name&gt;&lt;x:WorksheetOptions&gt;
&lt;x:Selected/&gt;&lt;x:Height&gt;7620&lt;/x:Height&gt;""+ ""&lt;x:Width&gt;15240&lt;/x:Width&gt;&lt;x:TopRowVisible&gt;0&lt;/x:TopRowVisible&gt;&lt;x:LeftColumnVisible&gt;0&lt;
/x:LeftColumnVisible&gt; &lt;x:ProtectContents&gt;False&lt;/x:ProtectContents&gt; &lt;x:DefaultRowHeight&gt;210&lt;
/x:DefaultRowHeight&gt; &lt;x:StandardWidth&gt;2389&lt;/x:StandardWidth&gt; &lt;/x:WorksheetOptions&gt; &lt;
/x:ExcelWorksheet&gt;&lt;/x:ExcelWorksheets&gt; &lt;x:MaxHeight&gt;80%&lt;/x:MaxHeight&gt;&lt;x:MaxWidth&gt;
80%&lt;/x:MaxWidth&gt;&lt;/x:ExcelWorkbook&gt;&lt;/xml&gt;&lt;![endif]--&gt;&lt;table class=wc4590F88 x:str&gt;
&lt;col width=&quot;56&quot;&gt;&lt;tr height=&quot;14&quot;&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;
/body&gt;&lt;/html&gt;'> <param name='DataType' value='HTMLDATA'> <param name='AutoFit' value='0'>
<param name='DisplayColHeaders' value='-1'><param name='DisplayGridlines' value='-1'><param name='DisplayHorizontalScrollBar' value='-1'>
<param name='DisplayRowHeaders' value='-1'><param name='DisplayTitleBar' value='-1'><param name='DisplayToolbar' value='-1'>
<param name='DisplayVerticalScrollBar' value='-1'> <param name='EnableAutoCalculate' value='-1'> <param name='EnableEvents' value='-1'>
<param name='MoveAfterReturn' value='-1'><param name='MoveAfterReturnDirection' value='0'><param name='RightToLeft' value='0'>
<param name='ViewableRange' value='1:65536'></object>""); HtmlEdit.focus(); }
关于怎样实现上传图片在这里就不多讲,CSDN上这类帖子太多了。只是要注意一点,因为使用的是模式对话框,所以在该页面不能有回发事件,操作最好在iframe里做。
——————————————————————————–
总结
谢谢你能看到这里,至此一个简单的HTML编辑器就制作完成了,本文主要讲述了如何得到一个HTML编辑器的代码,如何把它封装成一个.NET组件以及通过两个列子讲解了给它添加插件的方法。从上面的步凑你可以看出制作一个HTML编辑器其实很简单,虽然借鉴了一些别人的代码,但如果你仔细分析一下那些JS脚本,你就会豁然开朗的,如果你有更好的想法希望能告诉我。

MicrosoftOfficeWord2003中的XML

Microsoft Office Word 2003 中的 XML

发布日期: 11/26/2004 | 更新日期: 11/26/2004

Aaron Skonnard

几年以前,我开始着手一个新书项目时,就决定用 XML 来编写整个项目。我认为用 XML 完成该书可让我在其他地方重复使用内容、将内容转换为其他输出格式,并且可以方便地利用众多 XML 技术(包括 XPath、XSLT、各种 XML API、甚至是 XQuery)。

当我开始调查如何完成该项目的时候,我打开了 Microsoft  Office Word 2000,并开始进行试验。我不断寻找既可以使用 Word 的多信息编辑和格式功能来创作章节,又可以定义 Word 文档和 XML 结构之间的映射的方法。这可让我在熟悉的 Word 环境中工作来生成 XML 内容。当我试验“Save as HTML”功能时(当时使 Word 生成标记文档只有这种技术可以使用),显然还需要“Save as XML”选项。因此,我返回去使用出色的旧版 Emacs 来手工编写 XML。在完成数以千计的尖括号之后,我发誓再也不这么做了。

尽管用 XML 编写整本书非常有价值,但过程实在太痛苦了。幸好,现在可以使用 Microsoft Office Word 2003 了。

Word 2003 中的 XML 支持

Word 2003 中新增的 XML 支持是其最令人兴奋和最强大的功能之一。XML 不再是事后想到的内容;Word 已经针对 XML 进行了完整设计。它支持称为 Word 标记语言 (WordML) 的原生 XML 词汇。有关 WordML 的详细信息,可以从 Microsoft Office Word 2003 Preview 找到。

Microsoft Office Word 2003 引入了生成 WordML 文档的“Save as XML”命令。从这以后,当您双击一个由 Word 生成的 XML 文档时,Windows 加载程序会自动将该文件与 Word 进行关联。WordML 非常强大和灵活,足以捕获整个往返过程中 Word 文档的所有多信息编辑和格式。如果在 Word 中创建一个普通文档,将其保存为 WordML,然后再从 Word 中打开它,该文档保证与原始文档一样。

下面是 WorldML 文档的基本结构:

< xml version="1.0" encoding="UTF-8" standalone="yes" >
< mso-application progid="Word.Document" >
<w:wordDocument 
    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" ...
>
   <!-- WordML structure goes here -->
</w:wordDocument>

请注意,这就是一个 XML 文档。根元素的名称 wordDocument 来自 http://schemas.microsoft.com/office/word/2003/WordML 命名空间(在本专栏中,我将使用前缀“w”来引用这个命名空间)。当您从 Windows 资源管理器中双击该文件时,Windows  会自动确定它是 WordML 文档(通过检查顶部的 mso-application 处理说明),然后启动 Word 来处理它。

WordML 的引入可能是 Microsoft Office Word 2003 中最为重要的更改,但绝不是唯一的更改。开发人员还可以将自定义的 XML 架构定义和 XSLT 转换附加到 Word 文档。开发人员可以利用附加架构中的元素来标记内容,这样就可以插入用于简化处理过程的有意义的业务特定标记。当您在 Word 中保存文档时,它可以根据附加的架构来验证文档,并可以在处理过程中应用自定义的 XSLT 转换。本专栏的其余部分将更详细地分析这些吸引人的新功能。

WordML 简介

WordML 架构旨在镜像传统 .doc 文件中的信息。WordML 的架构是 Microsoft Word XML 内容开发工具包 (CDK) Beta 2 的组成部分,您可以从 MSDN 的 Microsoft Office 2003 Downloads 进行下载。WordML 文档的根元素始终为 w:wordDocument。w:wordDocument 包含表示完整 Word 文档结构(包括属性、字体、列表、样式以及包含章节和段落的实际文档正文)的几个其他元素,如图 1 所示。

图 2 显示了具有普通格式的 Word 文档,包括指定样式(例如 Heading 1)、内嵌样式(粗体和斜体)和简单的正文。

fig02

图 2 WordML 格式的文档

图 2 中所示的文档的 o:DocumentProperties 元素包含可用于所有 Office 文档的文档属性,例如标题、作者、上次修改者、创建日期、上次保存日期等等(请参见图 3)。o:DocumentProperties 元素来自不同于 WordML 的命名空间,因为它会应用到所有 Office 文档,并且将跨其他 Office XML 词汇(例如 ExcelML)进行共享。

w:fonts 和 w:styles 元素包含文档中使用的字体和样式信息。文档中使用的每种字体或样式都将使用其中一个元素中的 XML 元素来表示。图 4 展示了您在图 2 中所见的文档的字体和样式信息。

w:docPr 元素包含给定文档的 Word 特定属性,例如视图和缩放设置,如下面的代码片段所示:

<w:docPr>
  <w:view w:val="print"/>
  <w:zoom w:percent="100"/>
  <w:doNotEmbedSystemFonts/>
  <w:proofState w:spelling="clean" w:grammar="clean"/>
  <w:attachedTemplate w:val=""/>
  <w:defaultTabStop w:val="720"/>
  <w:characterSpacingControl w:val="DontCompress"/>
  ...
</w:docPr>

最后,w:body 是文档内容所在的位置。w:body 元素包含章节,而章节包含段落和表格。段落和表格是分别使用 w:p 和 w:tbl 元素创建的。段落和表格最终由文本运行元素 (w:r) 组成,该元素可以使用其他属性和样式加以批注。图 5 展示了图 2 中所示的文档的 w:body 元素。

正如您看到的那样,原始 Word 文档中的所有格式都会以某种形式在 WordML 文档中表示出来。这样就可以在不丢失任何信息的情况下,在 .doc 文件和 .xml 文件之间移动数据。

使用 WordML

文档使用 WordML 格式的主要好处在于,您可以使用任何编程语言在任何平台上利用 XML API(DOM、SAX、XmlReader 等等)来处理它们。您还可以使用像 XPath 和 XSLT 这样的高级 XML 服务来处理 WordML 文档。例如,使用以下 XPath 表达式,可以很轻松地打开一个 WordML 文档,并标识以 Heading 1 样式标记的所有 w:p 元素:

//w:p[w:pPr/w:pStyle/@w:val = 'Heading 1']

您还可以编写 XSLT 转换,以便在 WordML 和其他基于文本的格式之间转换。 例如,图 6 中所示的 XSLT 展示了一种将 WordML 转换为简单 HTML 结构的方法。 对图 2 中所示的 WordML 文档运行该转换会生成图 7 中所示的 HTML 文档。 WordMLHTML(另一个转换可以从 WordML to HTML XSL Transformation 中获得。) 

fig07

图 7 文档转换到 HTML

除了更方便地处理 Word 文档外,WordML 还可以更加方便地生成 Word 文档。因为它们就是 XML,您可以使用任何用于生成 XML 的标准技术来生成 Word 文档。例如,您可以轻松地即时编写可生成 WordML 文档的 ASP.NET 页面,图 8 中阐释了这种技术。

fig09

图 9 ASP.NET 页

该 ASP.NET 页会生成相同的文档,只是现在标题包含“Hello [name]”,其中 [name] 将替换为在查询字符串中提供的值。使用查询字符串“ name=Aaron”浏览到该 ASP.NET 页,会生成图 9 中所示的文档。

自定义架构

除了 WordML 以外,Microsoft Office Word 2003 还包括对自定义 XML 架构定义 (XSD) 的支持,这使您可以将一个或多个自定义架构附加到给定的 Word 文档中。然后,您可以用附加架构中的元素对文档进行批注。这样,您可以将与业务相关的标记插入到文档中,以便可以根据业务标识符(而不是更通用的 WordML 标记)来处理文档。

例如,请考虑一个包含新员工信息的 XML 文档。您可以使用图 10 中所示的员工架构中的元素来批注该文档。要完成该操作,您必须首先将架构附加到 Word 中的 XML 文档。

通过从菜单中选择“Tools | Template and Add-Ins”,您可以将 XML 架构定义附加到一个 Word 文档。出现的对话框将包含一个“XML 架构”选项卡,您可以在其中管理架构库,并选择要附加到该特定文档的架构(请参见图 11)。您还可以指出 Word 是否应该验证文档,以及它是否应该允许用户保存无效的文档。

fig11

图 11 管理架构

在添加架构后,就可以开始利用该架构中的元素批注文档。Word 中的“XML 结构”窗格(显示在右侧)可让您将附加架构中的自定义元素插入到文档中。“XML 结构”窗格还为您提供了一个针对当前整个文档中存在的自定义元素的界面,方法是在该窗格内显示这些元素的逻辑树结构。

图 12 显示了“XML 结构”窗格,以及如何选择一个自定义元素以插入到文档中。在此例中,我从 urn:employees 命名空间中选择了 Employee 元素来插入到文档中。当我双击某个自定义元素时,Word 会将当前突出显示的文本包括在该元素中。在第一次插入自定义元素时,Word 会询问您是否要包围整个文档,这通常是您想要执行的操作。

fig12

图 12 将自定义元素插入到文档中

在您完成该操作后,Word 将在文档中显示 XML 标记,以便您可以看到它们出现的位置。图 12 展示了在插入 Employee 元素包围整个文档后该文档的样子。如果您不想看到可见的 XML 标记,可以通过取消选中“Show XML tags in the document”复选框来隐藏它们。

fig13

图 13 嵌入式元素的树结构

在将其他元素插入到文档时,Word 会在“XML 结构”窗格的顶部显示自定义元素的逻辑树结构。该视图只显示嵌入式自定义元素,这样在其中选择文本会非常方便(如图 13 中的 Last 元素所示)。

架构验证

在插入自定义元素后,Word 将开始验证文档内容(假设您在附加架构时选择了该选项)。在文档左下方出现的紫色线(如图 12 所示)表示 Employee 元素的内容在当前是无效的(因为它在此例中缺少一些必需的子元素)。“XML 结构”窗格中 Employee 元素旁边的黄色图标表示相同的情况。右键单击这两项中的任一项就会显示该问题。

当您将光标放在 Employee 元素内时,Word 会根据该架构自动确定 Employee 下允许的子元素,并在“XML 结构”窗格的底部显示它们。在此例中,Word 显示了 ID、Name 和 Phone 元素,这样可以非常轻松地选择一些文本并快速地应用它们(请参见图 12)。

Word 还会根据架构中提供的简单类型定义来验证文本内容。例如,图 10 中的架构包含两个简单类型:一个称为 SSN,另一个称为 Phone。这两个简单类型都使用模式面(正则表达式)来限制字符串。图 14 展示了操作中的简单类型验证。请注意,如果它没有位于简单类型的值空间中,那么紫色线会出现在文本下方。在这种情况下,如果您右键单击它,Word 会指出该文本没有遵循必需的模式 (\d{3}-\d{2}-\d{4})。在您键入时,会即时发生这些情况。

fig14

图 14 类型验证

在您添加所有必需的元素并使文本符合正在使用的简单类型定义后,该文档应该如图 13 所示。在所有紫色线都消失后,您就知道该文档是有效的了。默认情况下,Word 不会让您将存在验证问题的文档保存为 XML。但是,选择“Allow saving as XML even if not valid”选项(请参见图 11)可以在文档尚未完全有效的情况下保存文档。当您保存带有自定义元素的文档时,您必须决定是否要在输出中包含 WordML 标记。

保存文档

在保存带有自定义元素的 Word 文档时,您有两个选项。默认选项是将文档保存为带有嵌入到树中的自定义元素的 WordML。另一个选项是“Save data only”,它将删除 WordML 标记,并且只保留自定义元素的树结构。

例如,图 15 展示了 Employee 和 ID 元素在图 13 所示文档的 WordML 中出现的位置。需要注意的一件事情是,根据该架构,Employee 元素不会正式允许包含除 ID、Name 和 Phone 之外的子元素,因此 w:p 子元素会使该文档无效。您可能会问,既然在保存前已经没有任何紫色指示符了,那么 Word 如何将文档视为有效呢?这表明,在忽略 WordML 标记存在的情况下(因为您可以选择排除它),Word 具有足够的智能来确定有效性。

fig16

图 16 仅保存为数据

当您选择“Save data only”(请参见图 16)时,Word 会删除 WordML 标记,并只保存文档中的自定义元素。但是,这样做会导致您丢失可能已经应用到文档中的所有特殊格式。图 17 显示了在以这种方式保存时 employee 文档的样子。请注意,完全符合附加架构的 XML 文档就更加简单和清晰了。

fig17

图 17 没有格式的文档

应用转换

在保存文档时,您可能希望同时生成完全不同的格式(可能甚至不是 XML)。 为了满足这个要求,Word 可以在保存过程中应用 XSLT 转换。 通过选择“Save As”对话框中的“Apply transform”选项,您可以指定一个转换(请参见图 16)。 简单地从磁盘中选择一个 XSLT,Word 将运行该转换,并在您每次保存该文档时输出结果。

您可以通过架构库将多个转换与给定的架构相关联,从而创建所谓的 Word 解决方案。 这样,对于所有实现给定架构的文档来说,这些转换都可用。 如果您可以将多个 XSLT 转换用于某个给定文档,则 Word 将在“XML 文档”窗格(位于右侧)中启用它们,这可让您在 Word 中同一文档的多个视图之间轻松地切换。

Word 还支持在打开文档的同时使用转换。 Word 在“Open”对话框中提供了“Open as XML”选项,以便您可以选择想要使用的特定转换。 当您选择该选项时,Word 会打开应用转换的结果,而不是您选择打开的原始文档。

小结

在添加 XML 支持以后,Microsoft Office Word 2003 就大大超越了先前的版本。 对 WordML 的原生支持可以在无需抛开 XML API 优点的同时构建强大而灵活的内容解决方案。 另外,对 XML 架构、实时验证以及各种 XSLT 技术的内置支持,使得利用自定义 XML 架构和输出格式信手拈来。

除了此处描述的核心 XML 功能以外,Microsoft Office Word 2003 还可以完全通过 Visual Basic  for Applications 和 Microsoft Word XML CDK Beta 2 进行编程。Microsoft Office Word 2003 将对使用文档驱动其业务的组织产生巨大影响。 有关完整示例,请查看包含在 CDK (Microsoft Office 2003 Downloads) 中的 DocLibrary (Contoso, LLC) 示例。

请将给 Aaron 的问题和意见发送至 xmlfiles@microsoft.com

Aaron Skonnard 是 Northface University (http://www.northface.edu) 的助教兼 .NET 项目的主管,他正在努力将这所学校建成全球最好的软件开发人员大学。 Essential XML Quick ReferenceEssential XMLAaron 与他人合著了 Essential XML Quick Reference(Addison-Wesley,2001 年)和 Essential XML(Addison-Wesley,2000 年)。您可以通过http://www.skonnard.com与 Aaron 取得联系。

结构或大内存块打包的办法(中)

<iframe align=”top” marginwidth=”0″ marginheight=”0″ src=”http://www.zealware.com/csdnblog01.html” frameborder=”0″ width=”728″ scrolling=”no” height=”90″></iframe>

The information in this article applies to:< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

– Microsoft Visual C++ 6.0,SP5

结构大内存块打包的办法(中)

Revision History:

对本文档所有修改都应按修改时间顺序记录在此。

Version

Date

Creator

Description

1.0.0.1

2004-2-23

郑昀

草稿

Implementation Scope

继续阅读之前,我们假设您熟悉以下知识:

n SAFEARRAY

n ISTREAM

n Microsoft MSMQ

目录:

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream流打包传递数据到MSMQ队列

3.借用IStream流传递数据


正如前面所述,当你有一块非常巨大的数据要传递给MSMQ队列时,而且你希望一次液压成型,那么把它打包入IStream,也是一个很常用技巧,了解COM的人都知道,我也不多解释了。

我们研究了ATLIPersistMemoryImpl接口Load方法的实现机理,来做我们的事情:

// 函数名:LoadStreamOnHugeMemory

// 功能: 同上

// 第一个参数pvMem指向一块要打包的内存,第二个参数指明这块内存的大小

HRESULT LoadStreamOnHugeMemory(void pvMem, ULONG cbSize)

{

// Get Memory Handle:

HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, cbSize);

If(NULL == h) return E_OUTOFMEMORY;

LPVOID pv = GlobalLock(h);

If(!pv) return E_OUTOFMEMORY;

// Copy to memory block

CopyMemory(pv, pvMem, cbSize);

CComPtr<istream> spStream;<p></p></istream>

// Create stream on Memory:

HRESULT hr = CreateStreamOHGlobal(h, TRUE, &spStream);

If(FAILED(hr))

{

GlobalUnlock(h);

GlobalFree(h);

return hr;

}

// stream now owns the memory

// unlock the data

GlobalUnlock(hGlobal);

// Create a stream holder. Load the stream holder from the global stream.

// THIS STREAM HOLDER IS INTERITED FROM IPersistStream

// And all virtual functions are Modified to handle the object….

CComPtr <istreamholder> pHolder = new CComObject <cstreamholder>;<p></p></cstreamholder></istreamholder>

CComPtr <ipersiststream> pHolderStream;<p></p></ipersiststream>

hr = pHolder->QueryInterface (IID_IPersistStream, (void **)&pHolderStream);

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

pHolderStream->Load(pStream);

CComVariant vComData = pHolder;

.. ..

//

// now, you have a big chunk of memory loaded into a ComVariant

//

// 现在你可以把打包后的CComVariant传递给MSMQMessegeBody属性了:

pisMsg->Body = vComData;

.. ..

}

其实,Aydin的实现和ATLIPersistMemoryImpl接口Load方法实现异曲同工。我们不妨换一种方式实现。

下篇给出Aydin的代码,欢迎继续收看。(为什么CSDN一篇文章限制64KB呢?)

结构或大内存块打包的办法(下)

Writen by zhengyun.NoJunk(at)tomosoft.dot.com

Disclaimers


Programmer’s Blog List

< xml:namespace prefix = v ns = “urn:schemas-microsoft-com:vml” /><shapetype id=”_x0000_t75″ stroked=”f” filled=”f” path=”m@4@5l@4@11@9@11@9@5xe” o:preferrelative=”t” o:spt=”75″ coordsize=”21600,21600″><stroke joinstyle=”miter”></stroke><formulas><f eqn=”if lineDrawn pixelLineWidth 0″></f><f eqn=”sum @0 1 0″></f><f eqn=”sum 0 0 @1″></f><f eqn=”prod @2 1 2″></f><f eqn=”prod @3 21600 pixelWidth”></f><f eqn=”prod @3 21600 pixelHeight”></f><f eqn=”sum @0 0 1″></f><f eqn=”prod @6 1 2″></f><f eqn=”prod @7 21600 pixelWidth”></f><f eqn=”sum @8 21600 0″></f><f eqn=”prod @7 21600 pixelHeight”></f><f eqn=”sum @10 21600 0″></f></formulas><path o:connecttype=”rect” gradientshapeok=”t” o:extrusionok=”f”></path><lock aspectratio=”t” v:ext=”edit”></lock></shapetype><shape id=”_x0000_i1025″ style=”WIDTH: 0.75pt; HEIGHT: 0.75pt” alt=”” type=”#_x0000_t75″></shape>

博客堂

小气的神

飞鹰手记 飞鹰手记之.NET专版

蝈蝈俊

思归

[MVPLeader]Grace Zhang

豆腐

跟随大象的舞步

Don Box’s Blog

Eric.Weblog()

The .NET Guy

Blogs@asp.net

本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyun对所讨论问题的当前看法,zhengyun不保证所给信息在发布之日以后的准确性。

用户应清楚本文档的准确性及其使用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款:

  1. 复制时不得修改原文,复制内容须包含所有页

  2. 所有副本均须含有 zhengyun的版权声明以及所提供的其它声明

  3. 不得以赢利为目的对本文档进行传播

Trackback: http://tb.blog.csdn.net/TrackBack.aspx PostId=12749

详解C#委托,事件与回调函数

详解C#委托,事件与回调函数< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

.Net编程中最经常用的元素,事件必然是其中之一。无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等。
    “protected void Page_Load(object sender, EventArgs e)”
这段代码相信没有人不熟悉的。细心一点一定会发现,非常多的事件方法都是带了“object sender, EventArgs e”这两个参数。这是不是和委托非常相似呢?

一、委托(有些书中也称为委派

委托是什么呢?这个名字的意思已经赋予了我们想象的空间,你是编程的,你现在正在写一个ASP.NET网页,而JS是你不熟悉的,于是你委托你的一位同事来帮助你完成JS部分。这就是委托,把你所不能做的事情交给其他人去做。而怎么知道是哪个人去做呢?当然是要知道名字!而为了区别名字一样的不同人,因此,需要描述一个特征。

C#中,委托的作用是这样描述的:委托就像一个函数的指针,在程序运行时可以使用它们来调用不同的函数。这个其实和你委托同事完成 JS代码一样。如果有两位同事可以做这件事情,他们只要做的结果能够满足你的需求(就像一个接口),尽管他们做的过程不一样,并且作出的效果也不一样,但是,能够达到你的要求就可以了。

1.简单的委托

那委托需要承载哪些信息呢?首先,它存储了方法名,还有参数列表(方法签名),以及返回的类型。比如:
             delegate string/*
返回类型*/ ProcessDelegate(int i);
   
这就是一个委托的定义。蓝色部分是声明委托的关键字,红色部分是返回的类型,而黑色部分是委托的类型名,和一个类名差不多,而()里的就是参数部分。它的意思是,你要使用这个委托来做事情的话,那么,做事情的方法必须满足以下条件:
     1
、返回类型和委托的返回类型一致,这里是string类型;
     2
、能且只能有一个参数,并且是int类型。
OK,
满足以上两个条件,一切就可以工作了🙂

例如:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 
 5 namespace TestApp
 6 {
 7     /// <summary>
 8     /// 
委托
 9     /// </summary>
10     /// <param name=”s1″></param>
11     /// <param name=”s2″></param>
12     /// <returns></returns>
13     public delegate string ProcessDelegate(string s1, string s2);
14 
15     class Program
16     {
17         static void Main(string[] args)
18         {
19             /*  
调用方法  */
20             ProcessDelegate pd = new ProcessDelegate(new Test().Process);
21             Console.WriteLine(pd(“Text1”, “Text2”));
22         }
23     }
24 
25     public class Test
26     {
27         /// <summary>
28         /// 
方法
29         /// </summary>
30         /// <param name=”s1″></param>
31         /// <param name=”s2″></param>
32         /// <returns></returns>
33         public string Process(string s1,string s2)
34         {
35             return s1 + s2;
36         }
37     }
38 }

输出的结果是:
Text1Tex2

2、泛型委托

泛型的委托,就是然参数的类型不确定,例如代码改写为:

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

 namespace TestApp
{
    /// <summary>
    /// 
委托
    /// </summary>
    /// <param name=”s1″></param>
    /// <param name=”s2″></param>
    /// <returns></returns>
    public delegate string ProcessDelegate<T,S>(T s1, S s2);

class Program
    {
        static void Main(string[] args)
        {
            /*  
调用方法  */
            ProcessDelegate<string,int> pd = new ProcessDelegate<string,int>(new Test().Process);
            Console.WriteLine(pd(“Text1”, 100));
        }
    }

public class Test
    {
        /// <summary>
        /// 
方法
        /// </summary>
        /// <param name=”s1″></param>
        /// <param name=”s2″></param>
        /// <returns></returns>
        public string Process(string s1,int s2)
        {
            return s1 + s2;
        }
    }
}

输出的结果就是:
Text1100

泛型的详细内容不属于本文的介绍范围,这里不加多说了。

二、事件

在某件事情发生时,一个对象可以通过事件通知另一个对象。比如,前台完成了前台界面,他通知你,可以把前台和你开发的程序整合了。这就是一个事件。可以看出事件是在一个时间节点去触发另外一件事情,而另外一件事情怎么去做,他不会关心。就事件来说,关键点就是什么时候,让谁去做。

C#中,时间定义关键字是event。例如:
event ProcessDelegate ProcessEvent;

整个事件定义方法以及执行过程:

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

namespace TestApp
{
    /// <summary>
    /// 
委托
    /// </summary>
    /// <param name=”s1″></param>
    /// <param name=”s2″></param>
    /// <returns></returns>

public delegate void ProcessDelegate(object sender, EventArgs e);

class Program
                         {

static void Main(string[] args)
         {
              /*  
第一步执行  */
             Test t = new Test();
              /* 
关联事件方法,相当于寻找到了委托人 */
              t.ProcessEvent += new ProcessDelegate(t_ProcessEvent);
              /* 
进入Process方法 */
               Console.WriteLine(t.Process()); 

            Console.Read();
          }

 static void t_ProcessEvent(object sender, EventArgs e)
        {
                Test t = (Test)sender;
                t.Text1 = “Hello”;
                t.Text2 = “World”;
        }
    }

 public class Test
    {
        private string s1;

public string Text1
          {
              get { return s1; }
              set { s1 = value; }
           }

private string s2;

public string Text2
          {
              get { return s2; }
              set { s2 = value; }
          }

public event ProcessDelegate ProcessEvent;

void ProcessAction(object sender, EventArgs e)
          {
              if (ProcessEvent == null)
                   ProcessEvent += new ProcessDelegate(t_ProcessEvent);
              ProcessEvent(sender, e);
          }

   //如果没有自己指定关联方法,将会调用该方法抛出错误
      void t_ProcessEvent(object sender, EventArgs e)
         {

             throw new Exception(“The method or operation is not impl—-emented.”);

   }

void OnProcess()
         {
              ProcessAction(this, EventArgs.Empty);
        }

public string Process()
        {
             OnProcess();
             return s1 + s2;
        }
    }
}

感觉到了什么?是不是和代码注入了差不多,相当于是可以用任意符合委托接口(委托确实很像接口)的代码,注入到Process过程。在他返回之前给他赋值。

三、回调函数

打了这么多字,好累啊!

回调函数就是把一个方法的传给另外一个方法去执行。在C#有很多回调函数,比如异步操作的时候。这里先举个例子:

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

namespace TestApp
{
    /// <summary>
    /// 
委托
    /// </summary>
    /// <param name=”s1″></param>
    /// <param name=”s2″></param>
    /// <returns></returns>
    public delegate string ProcessDelegate(string s1, string s2);

class Program
    {
        static void Main(string[] args)
        {
            /*  
调用方法  */
            Test t = new Test();
            string r1 = t.Process(“Text1”, “Text2”, new ProcessDelegate(t.Process1));
            string r2 = t.Process(“Text1”, “Text2”, new ProcessDelegate(t.Process2));
            string r3 = t.Process(“Text1”, “Text2”, new ProcessDelegate(t.Process3));

            Console.WriteLine(r1);
            Console.WriteLine(r2);
            Console.WriteLine(r3);
        }
    }

 public class Test
    {
        public string Process(string s1,string s2,ProcessDelegate process)
        {
            return process(s1, s2);
        }

 public string Process1(string s1, string s2)
        {
            return s1 + s2;
        }

 public string Process2(string s1, string s2)
        {
            return s1 + Environment.NewLine + s2;
        }

 public string Process3(string s1, string s2)
        {
            return s2 + s1;
        }
    }
}

输出结果:
Text1Text2
Text1
Text2
Text2Text1

Process方法调用了一个回调函数,当然这里只执行了回调函数。可以看出,可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。而设计上有一个抽离出可变部分代码的原则,这种用法无疑可以用到那种场合了。

Openlab病毒遭遇

刚刚在用FireFox浏览OpenLab(开放实验室,简称OL,和Office Lady一样的缩写,呵呵)的时候,突然发现网页字体变大了一号,于是按住Ctrl并滑动滚轮,把字体调小。但是当我新打开一个标签后发现,刚才调好的字体还是变大了!心里一惊,赶快在菜单栏里点”查看”->”文字大小”->”正常”,页面字体没有变化,依然是大了一号!

OpenLab是基于ASP.net的,一直由宝玉等人开发,在IE下测试。后来我用FF测试发现字体偏小,于是麻烦他把body字号改成了12px。但是目前这个12px好像失效了的。我查看Firebug的错误报告,大致是页面上有js错误。js错误导致了css的失效么??正在纳闷中,Openlab的论坛群里有人说,论坛上是不是有病毒?这一句一出口,接着大家都说自己的杀毒软件报警了,可是瞅瞅我的杀软,却一直都是静悄悄的啊。这一下我明白了,因为很多人使用的是IE浏览器,所以肯定是论坛上有病毒传播开了!而我因为使用的是安全的FireFox,所以躲过了一劫!

接着,宝玉找到了原因,论坛的html页面第一行被插入了两行iframe代码!两行代码可不是小事,我用ff打开代码中调用的页面,一步步查下来,找到了病毒释放者的一些信息:

病毒地址:http://w.168080.com/0.exe
clsid:BD96C556-65A3-11D0-983A-00C04FC29E36
注册域名:168080.com
域名信息:
Administrative Contact:
dai zhengbo
hunan loudishi
lou di Hunan 417704
CN
tel: 07386622928
fax: 07386622928
371199612@qq.com
真是可恶啊。

在iframe调用的网页里,还看到一个cnzz的计数器,是http://eol.hb.cn/,中国教育在线湖北分站,不知道是病毒释放者拿来赚流量的一个网站,还是只是用来转移人视线的。

总之,还是用FF安全,有惊无险。截至此文完成,OpenLab的服务器还在修复这次中毒事件,网页已经无法打开,还有部分网页在忙于杀毒=_=! 据估计,是因为路由器被劫持造成的病毒感染。

这一次,是真真切切感受到FF的安全性是多么重要了,hoho,所以推荐大家赶快改用FF来浏览网页吧,至于IE,就留作登录网上银行时候再拿出来:-)

firefox, iframe, openlab

使用.NET读取XML文件选择自lotusswan的Blog

介绍< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

本文中我将介绍在ASP.NET应用程序中如何读取XML文件,这是一个十分有用的技巧。使用这个技巧,我们能够定制我们的应用程序的配置文件,也可以读取那些保存在XML文件中的数据。

概论

下面的代码将使用XmlTextReader对象将磁盘文件中的数据读取到XmlDocument对象中。XmlTextReader对象在功能上和StreamReaderBinaryReader对象十分相似,只不过它是专为读取XML文件而特别设计的。除此以外,XmlTextReader对象还有其他一些与XMl相关的特性。例如,代码中使用到的WhitespaceHandling属性告诉应用程序不要为XML文件中多余的空格建立节点。

下面的代码使用XmlTextReader对象的DocumentElement属性来查找XML文档的树状表达形式的根节点。之后,递归地调用AddWithChildren方法将将节点及它的子节点一同添加到listbox中。

下面的代码还包含了属性的处理。属性节点并不包含在一个XmlDocument对象的节点的子节点集合中。因而,你只能使用XmlNode对象的Attributes属性获得属性节点集合。获取了属性节点集合后,代码使用XmlNamedNodeMap对象来保存这个集合。这个对象能够保存任何类型的XmlNode对象的任何集合。

代码列表

private void btnLoad_Click(object sender, System.EventArgs e)

{

XmlTextReader reader = new XmlTextReader(

Server.MapPath(“mycompany.xml”));

reader.WhitespaceHandling = WhitespaceHandling.None;

XmlDocument xmlDoc = new XmlDocument();

//将文件加载到XmlDocument对象中

xmlDoc.Load(reader);

//关闭连接

reader.Close();

//listbox中添加代表文档的元素

lbNodes.Items.Add(“XML Document”);

//查找根节点,并将它及它的子节点一同添加到listbox

XmlNode xnod = xmlDoc.DocumentElement;

AddWithChildren(xnod,1);

}

private void AddWithChildren(XmlNode xnod, Int32 intLevel)

{

//将节点及它的子节点一同添加到listbox

//intLevel 控制缩进的深度

XmlNode xnodWorking;

String strIndent = new string(‘ ‘,2 * intLevel);

//如果节点有值,读取它的值

string strValue = (string) xnod.Value;

if(strValue != null)

{

strValue = ” : ” + strValue;

}

//将节点的详细信息添加到ListBox

lbNodes.Items.Add(strIndent + xnod.Name + strValue);

//如果是元素节点,获取它的属性

if (xnod.NodeType == XmlNodeType.Element)

{

XmlNamedNodeMap mapAttributes = xnod.Attributes;

//将节点属性添加到ListBox

foreach(XmlNode xnodAttribute in mapAttributes)

{

lbNodes.Items.Add(strIndent + ” ” + xnodAttribute.Name +

” : ” + xnodAttribute.Value);

}

//如果还有子节点,就递归地调用这个程序

if(xnod.HasChildNodes)

{

xnodWorking = xnod.FirstChild;

while (xnodWorking != null)

{

AddWithChildren(xnodWorking, intLevel +1);

xnodWorking = xnodWorking.NextSibling;

}

}

}

}

}

谈谈网站静态化

写在前头

静态化是解决减轻网站压力,提高网站访问速度的常用方案,但在强调交互的We2.0 时代,对静态化提出了更高的要求,静态不仅要能静,还要能动,下面我通过一个项目,谈谈网站静态化后的架构设计方案,同时和大家探讨一下,在开源产品大行其道,言架构必称MemberCache, Nginx,的时代,微软技术在网站架构设计中的运用.

静态化的设计原则和步骤

静态化是解决减轻网站压力,但是静态化也会带来一系列的问题,包括开发上复杂度的增加,维护难度的增加,运用不的当,更可能适得其反,而许多替代方案,比如页面缓存,如果运用得当,也能起到很好的效果,所以在开始之前,必须进行详细的考察,确定是否适合静态化,并制定适合的静态化方式,下面先介绍一下

l 考查读写比:

读写比,准确的说是读写负荷比,是否值得静态化的最终考虑,由于一般写入的压力明显大于读出的压力,如果写入太频繁,或者每次写入消耗的资源太多,都不能达到效果,我觉得读写比例10:1应该是个上限.具体情况需要根据自己的业务逻辑判断

< xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

l 确定页面呈现的内容是否适合静态化:

在设计方案时,必须详细考虑每个原型页面,找到页面上展示的信息,和他的更新方式,更新时机,更新频率,一定要注意那些不起眼的信息,他们可能左右你的设计,

比如:我们以CSDN的论坛的任意一篇帖子为例,进行分析

< xml:namespace prefix = v ns = “urn:schemas-microsoft-com:vml” /><shapetype id=”_x0000_t75″ stroked=”f” filled=”f” path=”m@4@5l@4@11@9@11@9@5xe” o:preferrelative=”t” o:spt=”75″ coordsize=”21600,21600″><stroke joinstyle=”miter”></stroke><formulas><f eqn=”if lineDrawn pixelLineWidth 0″></f><f eqn=”sum @0 1 0″></f><f eqn=”sum 0 0 @1″></f><f eqn=”prod @2 1 2″></f><f eqn=”prod @3 21600 pixelWidth”></f><f eqn=”prod @3 21600 pixelHeight”></f><f eqn=”sum @0 0 1″></f><f eqn=”prod @6 1 2″></f><f eqn=”prod @7 21600 pixelWidth”></f><f eqn=”sum @8 21600 0″></f><f eqn=”prod @7 21600 pixelHeight”></f><f eqn=”sum @10 21600 0″></f></formulas><path o:connecttype=”rect” gradientshapeok=”t” o:extrusionok=”f”></path><lock aspectratio=”t” v:ext=”edit”></lock></shapetype><shape style=”WIDTH: 276pt; HEIGHT: 251.25pt” id=”_x0000_i1025″ type=”#_x0000_t75″><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png”></imagedata></shape>

上面的帖子中呈现的内容主要是这样几块,帖子内容,回复内容,发帖人回复人的用户信息

n 帖子内容和回复内容在发帖时更新,发帖后用户可以修改其内容,更新频率高

n 用户信息,用户修改个人信息时可能会发生更改,用户等级增加时也可能发生更改,比如加星,更新频率低

n 回复数将每次回复后都要更改,更新频率高

n 设计时要注意细节,如上图中圈出来的部分,这些部分是怎么修改的,频率有多大,一个都不能放过.

l 确定生成方式:

在上面帖子一例中.每次更改都重新生成页面是不可取的,一篇比回复数多的帖子,需要的数据量是巨大的(每层楼的用户信息,回复内容),任何修改,都需要重新取出数据进行生成是不能允许的.一般除非你的页面基本不用更新,或者更新开销极小,(比如一段嵌入的广告代码)才能采用整体更新的方式,不然就需要我们找到合适的更新页面局部区域的方法:

一般有下面两个方法:

1) 正则修改法:

 比如,如果帖子中的回复数,html代码是这样
 <label>回复数<var id="replyCount">34</var></label>
 我们可以通过用下面正则来查找并替换计数
  ( <=id="replyCount">)\d{1,}

2) 页面区域分块:

把页面分成很多小块,在显示时组装起来,比如DotText就采用这个方法

<shape style=”WIDTH: 243pt; HEIGHT: 282.75pt” id=”_x0000_i1026″ type=”#_x0000_t75″><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.png”><font color=”#000000″ size=”3″></font></imagedata></shape>

这是一篇典型的Dottext blog页面,其中红色标定部分是一个独立的文件,而黄色框内的是脚本动态加载,这些部分在最终显示的时候组合起来,最终构成了一篇Blog,具体的组合方法也有多种,可以使用Include,也可以自己来实现.DotText就自己实现了一套加载机制

上面的两种方法并不孤立,并可以根据需要,配合使用

l 确定需要动态加载的信息:

页面上总有一些内容看起来不太适合静态化,最典型的是一些统计结果,比如如果你在做一个图书介绍页面,可能就会需要展示图书的当天综合评分,或者书籍排名,这些内容需要用脚本进行动态加载

既然做了静态化,就是希望减少服务器负载,动态加载的数据总是不得已而为之,有的时候在需求允许的情况下,我们在数据在实时性和性能方面做一些妥协,比如上面帖子中的用户星级和昵称,从数据实时性上说,当用户的星级增长,他发言的所有帖子都应该发生变化,所以应该用动态加载.然而其实上这些信息如果不发生变化,也无伤大雅,用户反而能够看到自己在多年前发帖时的级别和昵称.

现实中的项目

X网站是大型的电影资讯,电影社区,向外提供电影相关信息服务,以及用户社区,其中信息服务部分, 其中大部分页面属于信息呈现页,读取量比较大,百万级别pv,信息主要由编辑在后台发布,更新较少,但其页面上有大量的交互性的内容,比如评论,收藏列表,同时许多内容允许用户创造,比如上传图片,添加注释.交互内容的数量和交互的频繁程度,都超过了普通的咨询页面,这次调整,准备将其中访问量最大的几块:电影资料页,影人资料页,进行静态化,如果成功,还将运用到更多的频道,基本实现全站静态化

通过对页面设计和前一版本的分析,下面是具有挑战性的地方.这些特点基本使用于大多数web2.0的站点,很具有典型意义

l 页面生成的触发条件复杂

一般论坛中的帖子或者blog,更新方式比较单一:主要是由回复进行触发还有少数的修改动作,然而该网站一个页面上需要根据不同触发条件就有20多个, 比如光二级菜单:用户发布图片,删除图片,发布或者删除影片信息,发布或者修改视频,后台修改电影信息,都有可能触发

l 一个动作触发生成的页面可能很多而且相互交叠

每一个动作都会触发一系列的生成,并且不同动作可能都会涉及同一个页面或者区域的生成.

比如:用户给一步电影评分,需要生成评分更多页,评分统计更多页,首页右侧谁还关注此影片小区域,等等.用户收藏一个影片,也需要更新首页右侧谁还关注此影片小区域

l 触发频繁:

虽然不及某些更大规模的网站,但是由于涉及众多用户参与的内容,评论,收藏等等,触发点多,发生频度相当频繁

l 页面多,结构复杂,空间占用大:

通常,需要生成的页面规模是这样粗略估算的,Rn*P,Rn为资源数,P为每个资源的页面数,所谓资源,可以看做一个生成单位,其页面数可以简单看做发布一个资源,就需要生成其所有相关页面数量,比如:发布一个blog,就需要生成一个Blog,同时还需要生成或者更新个人主页的blog列表,算上个人主页右侧的分类文章数的小块,也就是最多10来个页面或者区域,但是发布一个电影,其相关的页面至少有50个以上,而且有的页面还带有分页,一个信息比较丰富的电影,其页面竟可以达到千个以上,空间10~< xml:namespace prefix = st1 ns = “urn:schemas-microsoft-com:office:smarttags” /><chmetcnv w:st=”on” unitname=”m” sourcevalue=”20″ hasspace=”False” negative=”False” numbertype=”1″ tcsc=”0″>20M</chmetcnv>,而且资源总数也不少,电影80000左右,电影人虽然P值较少,但是总量确有几十万之巨,估计静态页面磁盘占用量几百个G

l 向下兼容

这是一个已有系统,旧系统的框框需要突破,但又没有时间,或者不能完全突破,比如Url,已经被收录到搜索引擎,就不能随便调整,还有一些地方,原本没有为静态生成考虑,另一些地方又需要兼容旧的设计.

l 多台前端Web

这种结构要求生成的文件可能需要分布到多个服务器(另一个方案是放在几台专用的机器上,等前端来取)

l 任务紧迫

架构讨论结束仪式六月初,离奥运开幕上线只有两月,也就是说所有底层框架实现,页面模板开发,调试测试,动作的整理,必须在7月底全部完成,按我原来估计,光实现这几块的上百个页面模板和填充方法,也需要那么长的时间

综合考虑上述因素,架构必须要有以下几个方面的特点

l 动作可以灵活扩展配置,某个动作对应哪些生成,应该可以配置,并且可以分组

l 文件必须有分发机制

l 分发和生成必须独立出来,并且支持分布式

l 各种的动作,必须转化为消息,发送到生成和分发服务器进行处理

l 针对同意资源频繁动作,在变量相同的情况下能够具有合并的能力

l 动作必须有记录

l 尽量考虑使用已有成熟技术,节省开发时间

下面是设计的第一个架构

<shape style=”WIDTH: 414.75pt; HEIGHT: 217.5pt” id=”_x0000_i1027″ type=”#_x0000_t75″ o:ole=””><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image005.emz”><font color=”#000000″ size=”5″></font></imagedata></shape>

用户的动作经过MSMQ[1]传入到生成分发中心(途中绿色箭头)进行处理,,处理中心接受到消息后,负责生成对应的页面或者页面区域,并将页面分发到各个服务器,负载均衡沿用以前的架构,采用微软的NLB[2]

之所以用MSMQ,就是看上了他提供的完整的消息存储恢复机制,这样我们能确保即使服务器down掉重启后,消息依然能正常处理,碰巧我们cms组的同事MSMQ非常熟悉,并且真准备在另外一个项目中使用类似的架构于是一拍即合

页面采用分块存储,这样能保证生成时目标小,开销小,也能重用性,然后再藉由SSI[3](shtml include)进行整合,之所以采取这样的方案,而不采用Dottext的整合方式,是因为如果采用Dottext的方式,就必须走IIS.Net的管道[4],而据测试,经过管道和直接返回html性能有非常大的差异,而使用ssi,在性能上是一个折中,并且可以Light HTTPd等高性能web服务器

模板生成方式,采用了XSLT和另外一种自定义的模板(我的同事开发的机制,很有趣, 理论上能把传统模板替换的性能开销全部消除),生成的最终产物是shtml,之所以生成shtml是为了使用其ssi(Server Side Include)的特性,保证一定的灵活性,并实现热点数据的分离:某些页面上的部分可能会频繁更新和生成,而其它地方不变,或者某个部分是所有页面通用的(比如页头和页脚),较之php下常常使用smarty,生成php文件,虽然灵活性不如php,但是性能上不相上下,还略高.

但是这个设计的问题是动态内容和静态内容没有分开,生成的html页面,和动态页面都放在前端服务器上,通过负载均衡访问,也造成了分发服务器需要分发到多台服务器,网络IO效率较低,而且静态内容需要的磁盘空间很大,且小文件非常多,和动态页面混在一起不便于优化,所以第二个方案对生成的静态内容与动态内容使用不同的服务器

方案二:

<shape style=”WIDTH: 414.75pt; HEIGHT: 200.25pt” id=”_x0000_i1028″ type=”#_x0000_t75″ o:ole=””><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image007.emz”><font color=”#000000″ size=”5″ face=”Arial”></font></imagedata></shape>

我们把生成的静态文件单独放置,可以看到,前端增加Nginx,作为跳转,把电影,影人资料库的页面转向静态服务器,其他的调用转向动态服务器,这样我们就可以单独为静态服务器进行优化,比如采用更高效的服务器等等.

同时减少了文件分发的次数(甚至可以只分发到本机),提高生成分发的处理能力

更进一步,可以把图片服务分到另外一组机器上,使用独立的域名,比如img.xxx.com,这样可以有效的减少带宽

最终完整架构:

<shape style=”WIDTH: 414.75pt; HEIGHT: 342.75pt” id=”_x0000_i1029″ type=”#_x0000_t75″ o:ole=””><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image009.emz”><font color=”#000000″ size=”3″></font></imagedata></shape>

文件生成分发中心

下图是文件生成分发中心的工作流程图

<shape style=”WIDTH: 414.75pt; HEIGHT: 217.5pt” id=”_x0000_i1030″ type=”#_x0000_t75″ o:ole=””><imagedata o:title=”” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image011.emz”></imagedata></shape>
生成服务对外只有一个输入,就是消息,一个输出:静态文件,内部根据消息,从配置文件中找到对应的生成方法,取出相应的模板,进行数据填充

分发服务主要吧生成服务产生的文件进行分发,分发到前端的N台服务器上,开始考虑得比较复杂,希望分发服务可以跨越协议(本地文件系统,局域网,http协议),跨越多种存储介质(文件系统,数据库),实际最后定下来基本是本地文件系统或者局域网传输

:上图中文件分发的部分也可以通过定制MogileFS,来实现分布式文件系统

马后炮:

总结起来,静态化除了对架构方面的影响,对开发和测试流程也有影响

对测试提出更高的要求:

因为一旦上线后,某个页面发现问题,即使是文字的修改,也需要重新生成许多页面,所以测试人员必须非常仔细,测试周期也需要延长

开发人员需要掌握模板语言

需要掌握一种模板预言,无论是Xslt还是自己开发的模板语言,都需要花一定的时间掌握

需要给第一次生成腾出足够时间:

如果不是新系统,那么数据迁移和生成的过程就比较痛苦,由于页面众多,第一次生成的过程可能需要以天来计算,在制定上线方案是就需要考虑到这个方面

<shape style=”WIDTH: 30pt; HEIGHT: 35.25pt” id=”_x0000_i1031″ type=”#_x0000_t75″><imagedata o:title=”MCj03840400000[1]” src=”file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image013.wmz”></imagedata></shape>Nginx作为前端的跳转,根据其他网站的经验,应该可以达到2-3万并发连接,但是使用之后,常常有卡壳的情况发生,具体症状为在浏览器中访问页面时,连接超时,或者一直不响应,此时Nginx连接数并不高,好在还有第一套方案可以备用,让我们有时间去解决这个问题,如果大家对这个问题有什么心得,欢迎交流

我的联系方式

MSN:yizhu2000@hotmail.com

Gtalk:yizhu2005@gmail.com

篇后:

在大型web开发上,我感到微软产品结构(包括微软开源社区的成果)在某些方面还存在一些不足:

高性能服务器选择太少

Linux下可以采用Light HTTPd,Nginx等诸多服务器,这些服务器在很多方面的表现会让Windows下唯一的选择–IIS相形见绌

分布式文件系统

微软及其社区没有比较著名的产品出现,Linux下有MogileFS

微软架构下,文件系统选择太少:

Linux下我们可以选择诸如Ext3,ReiserFS,Windows环境下,NTFS是唯一的选择,不过值得称道的是.NTFS的效率和稳定性都相当不错.

开源技术对windows版本的支持态度不积极

诸多在Linux下名声卓著的开源产品,又懒于为Windows提供相应的版本,或者提供的windows版本效果差强人意.使得采用微软服务器的厂商少了很多选择

现在的Web开发已经进入了各种技术大混合,大整合的时代,任何一个厂商都不可能涵盖所有方面,在后端架构和逻辑方面.NetJava严谨,良好的编程风格,清晰的设计思路,较高的运行效率,以及稳定的配套服务支持,是其最大的优势,对主要擅长微软技术的Web工程师和架构师而言,应该增进对Linux及开源社区的了解,才能根据需求设计出合理的架构


[1] Message Queuing: A Scalable, Highly Available Load-Balancing Solution

http://msdn.microsoft.com/en-us/library/ms811052.aspx

[2] 网络负载平衡(NLB)详解,注意文章后给出的参考链接

http://blog.chinaitlab.com/user1/563173/archives/2007/132713.html

[3] 怎样使用ssi,及其语法:

http://blog.csdn.net/dadou2007/archive/<chsdate w:st=”on” isrocdate=”False” islunardate=”False” day=”8″ month=”6″ year=”2008″>2008/06/08</chsdate>/2521365.aspx

Nginx下的ssimodule

http://www.nginx.cn/NginxChsHttpSsiModule

[4] asp.net的处理机制http://www.microsoft.com/china/msdn/library/webservices/asp.net/dnvs05Internals.mspx mfr=true

html.asp.aspx运行效率比较

http://iamlibai.blogbus.com/logs/2017870.html

解析和生成Excel报表方法汇总(转)

昨天在医院检查身体时,一个朋友打过来电话,咨询EXCEL报表开发都有哪些方法,他们要上一个有200多个不同格式Excel报表的项目,虽然在医院做全身检查中,我还是给他提供了几点建议。在这里我顺便总结下.NET平台下Excel报表开发的各种方法吧,供大家参考。
其实不仅仅是.NET平台,其他的任务开发平台和EXCEL报表交互,最终都会面临2个问题:如何解析EXCEL报表和如何生成EXCEL报表。
下面就以这2个问题为出发点,来探讨一下各种方法。
方法一:通过Excel对象模型(COM接口)解析和生成EXCEL报表。
.NET访问Office对象模型是借鉴于VBA的,VBA是整合到所有Office应用程序中的一个简单开发环境,每个Office程序都带有丰富的对象模型并通过COM技术实现对这些模型的访问。但遗憾的的是VBA有一定的局限性,VBA的开发环境过于简单,功能有限,VBA将代码嵌入到每个自定义文档中,这就使得修复漏洞和更新解决方案变得困难。微软为了让老的VBA程序员过渡到.NET平台继续开发Office程序,就进行技术更新,产生一种称为COM interop.NET技术与.NET中的Office对象模型进行交互。本质上所有的Office对象模型都是用非托管代码编写的(比如CC++),并提供COM接口。为了在托管代码(C#VB.NET)中与这些接口进行通信,必须使用封装器,通过它来实现托段代码与Office的非托管COM接口之间的协同工作。该封装器是一系列的.NET类,它有一个更好的名字叫Office主互调程序集(PIA:Primary Interop Assemblies)PIA便是.NET用于和Office交互的托管程序集。如下图:

这种方法能解析和生成EXCEL文档,功能强大,可以读写到任意单元格,生成单元格样式,合并单元格,支持图表等等。可以在.NET任何项目中使用,但会面临释放托管资源的问题。很多朋友认为这种方法必须要装EXCEL程序(Office),其实只需要安装PIA就可以了。PIA会在完全安装Office时自动安装,也可以在安装Office的向导中选择。但有个更好的方法是单独安装和分发PIA,微软想到了这一点,你可以通过下面的链接单独获取PIA的分发包。
Office 2003 Update: Redistributable Primary Interop Assemblies
http://www.microsoft.com/downloads/details.aspx FamilyID=3c9a983a-ac14-4125-8ba0-d36d67e0f4ad&DisplayLang=en
2007 Microsoft Office System Update: Redistributable Primary Interop Assemblies
http://www.microsoft.com/downloads/details.aspx familyid=59DAEBAA-BED4-4282-A28C-B864D8BFA513&displaylang=en
Office PIA会被安装到GAC目录,GAC位于Windows目下的Assembly子目录。
如果要使用PIA必须要先在项目中添加引用,如下图:
Excel 2007PIA12.0,如上图。Excel 2003PIA的是11.0
方法二:通过OLEDb解析Excel报表。
OleDb解析Excel报表,本质上是通过OleDb驱动完成的,此时可以把Excel当做一张数据库表来读取,Excel 2003用的驱动是Microsoft.Jet.OLEDB.4.0 Excel 2007用的驱动是Microsoft.Ace.OleDb.12.0,OleDb在解析Excel功能上比较简单,实际项目中往往运用此方法来获取Excel数据,如下图:
方法三:通过Access对象模型(COM接口)生成EXCEL报表。
此方法同方法一技术上类似,但是此时引用的PIA不是Excel的,而是Access的。如下图:
Access 2007PIA12.0,如上图。Access 2003PIA的是11.0
通过Access PIA,可以把Access 数据库中的表完美快速的生成到Excel工作薄中,可以同时生成多个工作表,速度非常之快,日期类型,货币类型,长格式数值,都会自动转成文本,不存在’#’或科学计数的问题。
方法四:通过开放Open Xml生成Excel报表
这种方法是基于Office Open Xml的,服务器和客户端都不需要安装Excel,可以在.NET任何项目中使用,开发难度高,但部署成本比较低。在格式方面,Open Xml对单元格样式控制能力很强,可以生成各种格式的单元,生成Excel的效率也非常高。
下图的中2个标注的地方都运用了这种技术生成Excel报表。

方法五:通过水晶报表和微软报表生成Excel报表
水晶报表功能强大,能生成复杂Excel报表,但是它是收费的。
而微软报表(Microsoft RepoertViewer)功能强大,本质上是微软借鉴水晶报表开发的,所以和水晶报表没什么两样,而且直接集成到了Visual Studio中。微软报表可以在.NET任何项目中使用,不仅可以生成EXCEL报表,还可以生成PDF等。下图是微软报表的一个应用,这个报表默认是横向打印:
说到这,不得不提微软数据库系统中的SSRS,这个报表服务引擎非常强大,不仅包含创建报表的功能,还包括管理和访问报表的功能,这是一个真正的企业级报表平台。
方法六:通过构建HTML标签向客户端生成Excel报表。
这种方法,在asp.net项目中很常见,尤其是在生成中国式的报表项目中非常常用。这种方法首先是构造HTML标签,生成指定的格式,填充数据,最后发送客户端,保存成Excel报表格式。当然这种发放就更不需要安装Excel程序了。
方法七:通过构造CVS格式生成EXCEL报表。
这种方法只能生成无格式的Excel报表,不需要安装Excel程序。
方法八:通过开源软件解析和生成Excel报表(基于BIFF)。
开源项目中有2个解析和生成Excel文件的精品:KoograMyxls,他们都是基于BINARY FILE FORMAT(BIFF)Excel文件结构形式开发的。运用这个开源项目进行解析和生成Excel报表都不需要安装Office,部署方便。
Koogra用于解析Excel文件。Myxls用于生成Excel文件。
先总结这8种方法,但是实际上还有其他的方法,比如强大的Active-x报表等等。

本文出自 “李涛的技术专栏” 博客,出处http://terryli.blog.51cto.com/704315/372820