精华内容
下载资源
问答
  • HTML文档类型声明

    DOCTYPE声明是指HTML文档开头的一行或两行代码,它描述使用哪个DTD(文档类型定义)。


    在解析网页时,浏览器通过分析页面的DOCTYPE声明来了解要使用哪个DTD,进而知道HTML是哪个版本。HTML有不同的版本,可以使用的标签数量以及标签的属性是不同的,且会影响到浏览器对CSS和JS的执行。所以,它是必须要正确声明的。


    XHTML 1.0 Strict(严格)的文件声明:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    XHTML 1.1的文件声明:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

    XHTML 1.0 Transitional(过渡型)文件声明:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    过渡型XHTML 1.0允许HTML 4的表现元素


    需要关注的是,<!DOCTYPE> 声明不是 HTML 标签,它不必关闭。

    HTML5不需要URL。浏览器一般不读取这些文件,,而是只识别常见的DOCTYPE声明,声明方式如下:

    <!DOCTYPE html>

    下面的链接是w3school给出的html元素和有效的DTD

    HTML元素对应出现在哪些文档类型

    对验证页面的有效性和区分浏览器模式上DOCTYPE声明非常重要。




    <html>标签的xmlns属性(摘自w3school)


    xmlns 属性可以在文档中定义一个或多个可供选择的命名空间。该属性可以放置在文档内任何元素的开始标签中。该属性的值类似于 URL,它定义了一个命名空间,浏览器会将此命名空间用于该属性所在元素内的所有内容。

    例如,如果需要使用符合 XML 规范的 XHTML 文档,则应该在文档中的<html> 标签中至少使用一个 xmlns 属性,以指定整个文档所使用的主要命名空间:

    <html xmlns="http://www.w3.org/1999/xhtml">

    如果需要在一个 div 元素中显示一串数学公式,则可以为该 div 元素定义一个数学命名空间。比如这样:

    <div xmlns="http://www.w3.org/1999/Math/MathMl">x3/x</div>

    如果您不希望在每次显示除法公式时都在 div 元素中定义 xmlns 属性,那么更好的办法是在文档的开头处定义具有前缀的命名空间:

    <html xmlns="http://www.w3.org/1999/xhtml">
    xmlns:math="http://www.w3.org/1999/Math/MathMl">

    然后,您就可以在 div 中使用该前缀了,就像这样:

    <math:div>x3/X<div>

    虽然在大多数情况下,绝大多数 XHTML 作者都不需要定义多个命名空间,但是您仍然有必要理解存在着多个命名空间,以便在需要选择将基于某个 DTD 的内容嵌入其他 DTD 定义的内容中时,可以管理多个命名空间。


    展开全文
  • 对于一个大项目来讲,数据库的设计命名规范是很重要的一个环节,好的表设计,人看得很舒服,一看就明白是什么意思了,下面看到一篇很不错的数据库对象命名参考文档,所以整理分享给大家。 引言 编码规范是一个...
            
     
    

    对于一个大项目来讲,数据库的设计命名规范是很重要的一个环节,好的表设计,让人看得很舒服,一看就明白是什么意思了,下面看到一篇很不错的数据库对象命名参考文档,所以整理分享给大家。

    引言

    编码规范是一个优秀程序员的必备素质,然而,有很多人非常注重程序中变量、方法、类的命名,却忽视了同样重要的数据库对象命名。这篇文章结合许多技术文章和资料,以及我自己的开发经验,对数据库对象的命名规则提出了一点建议,希望能为大家提供一些参考。

    NOTE:虽然这篇文章名为“数据库对象命名参考”,实际上,在这篇文章中我不仅介绍了数据库命名的规则,连带讲述了在数据库设计与开发时所需要注意的几个问题。

    基本命名规则

    表1. 基本数据库对象命名


    关于命名的约定

    变量(T-SQL编程中声明的变量)、过程(存储过程或触发器等)、实体(表、字段)应该根据他们所代表的实体意义和进程作用来命名:

    表2.好的命名 和 不好的命名 范例


    还有一个常见的错误就是只使用面向计算机的术语,而不是面向公司业务的术语,比如ProcessRecord就是一个含糊不清的命名,应该使用一个进程业务描述来替换它,比如CompleteOrder.

    如果完全根据上一条的要求,那么根据业务描述的过程名可能会变得很冗长,比如下面:

    prCountTotalAmountOfMonthlyPayments (计算每月付费的总金额)

    prGetParentOrganizationalUnitName (获取上级单位名称)

    此时则应该考虑使用缩写:

    • 如果可以在字典里找到一个词的缩写,就用这个做为缩写,比如:Mon(Monday)、Dec(December)
    • 可以删除单词元音(词首字母除外)和每个单词的重复字母来缩写一个单词。比如:Current = Crnt、Address = Adr、Error = Err、Average = Avg
    • 不要使用有歧异的缩写(一般是语音上的歧义)。比如b4(before)、xqt(execute),4tran(Fortran)

    表格、字段的命名:

    单数表名、字段名 还是 复数表名、字段名?

    可能大家很少会考虑到给表名起单数还是复数,比如,对存储客人信息的表,我们应该起Customer,还是Customers?我主张起单数表名,下面是来自《SQL Server 2000 宝典》的一段引用:

    主张用复数表名的阵营认为:表是由一组记录构成的,所以应当使用复数名词来命名它。他们经常使用的理由是:客户表是客户们的集合,而集合意味着多个,因此应当称他们为Customers表。除非你只有一个客户,但这种情况你根本用不着数据库。

    根据笔者的非正式调查,有3/4的SQL Server开发人员支持使用单数命名。这些开发人员认为,客户表是客户的集合,而不是客户们的集合。一组行不应当也不会被成为rows set(行们的集合),而会被称为row set(行集)。并且,通常在讨论时人们会使用单数名称来称呼表,说Customer表比说Customers表听起来更为清晰。

    避免无谓的表格后缀

    这两点我想大家都知道:1、表是用来存储数据信息的。2、表是行的集合。那么如果表名已经能够很好地说明其包含的数据信息,就不需要再添加体现上面两点的后缀了。

    实际工作中,我看到有的同事对表这样命名:GuestInfo,用于存储客户信息。这个命名与上面所说的第1点重复,谁都知道表本来就是存储信息(information)的,再加个Info无异于画蛇添足,个人认为直接用Guest做表名就可以了。

    对于存储航班信息的表,他又命名为FlightList。这个命名又与之前说的第2点相重复,表是行的集合,那么自然是列表(List),加上List后缀显得很多余,命名为 Flight 不是很好么?可见,他给自己都没有订立一个明确的命名规则,不然这两个表一定是要么命名为:GuestList、FlightList 要么命名为 GuestInfo、FlightInfo,而不会是两者的混合。

    多对多关系中连接表的命名

    大家知道,如果要实现两个实体间的多对多关系,需要三张表,其中一张是解析表。考虑下面这样一个多对多关系,这是一个经典的学生选课问题:一个学生可以选很多门课,一门课可以有很多学生。此时为了实现上面的关系,就需要一张解析表(这张表只存储学生ID和课程ID,而学生的信息和课程信息分别存在各自的表中),这个表的起名,建议的写法是将两个表的表名合并(如果表名比较长可做简化),此处如 StudentCourse。这个表中字段分别命名为StudentId、CourseID(既是此表的复合主键,同时分别为连接Student表和Course表的外键,等下到主键和外键的命名处再说),这样就实现了学生和课程之间的多对多关系,当然,这个关系还可以加点额外的东西,比如给StudentCourse表中加AccessLevel字段,值域D{只读,完全,禁止},就可以实现访问级别。

    约定俗成的字段名前/后缀

    数据库开发的时间久了,慢慢就会摸索出一个规律来:就是很多的字段都有些共同的特性。比如说,有的字段是代表时间的(例如发帖时间,评论时间),有的是代表数量的(例如浏览数,评论数),有的是代表真假类型的(例如是否将博客随笔显示在首页)。对于这种同一类型的字段,应该使用统一的 前缀 或者 后缀去标识它。

    我们来举几个例子看得更明白一点。

    以大家都熟悉的论坛来说,需要记录会员最后一次登录的时间,这时候一般人都会把这个字段命名为LoginTime 或者 LoginDate。这时候,已经产生了一个歧义:对于另一名开发者来说,如果仅看表的字段名称,不去看表的内容,很容易将LoginTime理解成 登录的次数,因为,Time还有一个很常用的意思,就是次数。

    为了避免这种情况发生,应该明确的规定:所有表示时间的字段,统一以 Date 来作为结尾。

    我们经常需要统计发帖数、回帖数信息,这时候,开发人员通常会这样去命名字段:PostAmount、PostTime、PostCount,同样,由于Time的歧义,我们首先排除掉不使用PostTime作为字段名。接下来,Amount 和 Count 都可以表示计数的意思,用哪个合适呢?这里,我推荐使用Count。为什么呢?如果你做过Asp开发,相信一定知道 RecordCount 这个属性,命名的时候有一个原则:就是使用约定俗成的名称,而不要去自创名称。既然微软都用Count后缀来表示数目,我们为什么不呢?

    于是,所有表示数目的字段,都应该以Count作为结尾。将这一概念做以推广,很容易得出,浏览次数为 ViewCount,登录次数为LoginCount 等等。

    再举一个例子,我们很少在数据库里直接保存图片等二进制数据,通常是仅保存图片的URL路径;在文章管理系统中,如果是转载文章,也会用到记录文章出处的字段。个人建议所有代表链接的字段,均为Url结尾。于是,图片路径的字段命名为 ImageUrl,文章出处字段的命名为SourceUrl。

    最后一个例子,我们经常需要用到布尔值,比方说,这篇随笔要不要显示到首页,这篇随笔是不是保存到草稿箱等等。同样,按照微软的建议,布尔类型的值均以 Is、Has 或者 Can开头。

    如果让我来建表示是否将随笔放到首页的字段,它的名字一定是这样的:IsOnIndex

    类似的例子是很多的,我在这里仅举出典型的几个范例,大家可以自行拓展,如果我能起到一个抛砖引玉的作用就很满足了。

    字段命名时需注意的一个问题

    我发现有很多开发人员喜欢给字段加上表名作为它的前缀,举个例子,如果有个表叫User,那么他就会将这个表中的字段命名为:UserId、UserPassword、UserName、UserPhone 等等。个人认为,这是没有必要的,因为你已经确切的知道了这个表存储的是User的信息,那么其中的字段必然是针对于User的。而且,在Join连接操作中,你的SQL代码看上去也会更加的精简一些,诸如 [User].UserName = Aritcle.ArticleAuthor 这样的代码完全可以实现为 [User].Name = Article.Author。

    这里还存在一个特例,就是表的外键包含的字段。在这种情况下,我倾向于使用表名+ID 的方式,比如 CategoryId 、UserId 等。假设有表Article,那么它的主键我会命名为Id,关联用户表User的外键包含的字段,我会命名为UserId。之所以这样,是因为在语言(比如C#)中创建对象时,有时候会使用代码生成器(根据数据库的字段名生成对象的字段、属性名),此时生成的代码更规整一些。

    建表时需要注意的问题

    数据库不仅是用来保存数据,还应负责维护数据的完整性和一致性

    我看过很多的开发人员设计出来的数据库,给我的感觉就是:在他们眼里,数据库的作用就如同它的名称一样――仅仅是用来存放数据的,除了不得不建的主键以外,什么都没有...没有 Check约束,没有索引,没有外键约束,没有视图,甚至没有存储过程。

    在这里,我提出如下数据库设计的建议:

    1. 如果要写代码来确保表中的行都是唯一的,就为表添加一个主键。
    2. 如果要写代码来确保表中的一个单独的列是唯一的,就为表添加一个约束。
    3. 如果要写代码确定表中的列的取值只能属于某个范围,就添加一个Check约束。
    4. 如果要写代码来连接 父-子 表,就创建一个关系。
    5. 如果要写代码来维护“一旦父表中的一行发生变化,连带变更子表中的相关行”,就启用级联删除和更新。
    6. 如果要调用大量的Join来进行一个查询,就创建一个视图。
    7. 如果要逐条的写数据库操作的语句来完成一个业务规则,就使用存储过程。

    NOTE:这里我没有提到触发器,实践证明触发器会使数据库迅速变得过于复杂,更重要的是触发器难以调试,如果不小心建了个连环触发器,就更让人头疼了,所以我更倾向于根本就不使用触发器。

    以Not Null的思路建表

    我发现很多开发人员在建表的时候,如果要新建一个字段,他的思路是这样的:默认这个字段是可以为Null的,然后去判断是不是非要Not Null不可,如果不是这样,OK,这个字段可以为Null,接着继续进行下一个字段。结果往往是一张表除了主键以外所有的字段都可以为Null。

    之所以会有这样的思路,是因为Null好啊,程序不容易出错啊,你插入记录的时候如果不小心忘输了一个字段,程序依然可以Run,而不会出现 “XX字段不能为Null”的错误消息。

    但是,这样做的结果却是很严重的,也会使你的程序变得更加繁琐,你不得不进行一些无谓的空值处理,以避免程序出错。更糟的是,如果一些重要数据,比如说订单的某一项值为Null了,那么大家知道,任何值与Null相操作(比如加减乘除),结果都是Null,导致的结果就是订单的总金额也为Null。

    你可以运行下面的代码尝试一下:

    Select Null + 5 As Result

    你可能会说,就算我将字段设置成Not Null,但是它依然可以接受空字符串,这样一来在程序中还是要进行空值处理。请别忘了,数据库还赋予你一个强力武器,就是 Check 约束,当你需要确保一个字段既不可以为Null,又不可以为空的时候,可以这么写:

    ColumnName    Varchar(50)       Not Null Constraint ck_ColumnName Check(Len(ColumnName) > 0)

    所以,合理的思维方式应该是这样的:默认这个字段是 Not Null的,然后判断这个字段是不是非为Null不可,如果不是这样,OK,这个字段是Not Null的,进行下一个字段。

    一个建表的范例脚本

    我正在建立我自己的个人空间,其中的文章表是这样写的:

    Create Table Article
    (
        Id            Int Identity(1,1) Not Null,
        Title         Varchar(50)       Not Null Constraint uq_ArticleTitle Unique,
        Keywords      Varchar(50)       Not Null,
        Abstract      Varchar(500)      Not Null,
        Author        Varchar(50)       Not Null Default '张子阳',
        Type          TinyInt           Not Null Default 0 Constraint ck_ArticleType Check(Type in (0,1,2)),  -- 0,原创;1,编译;2,翻译
        IsOnIndex     Bit               Not Null Default 1,   -- 是否显示在首页
        Content       Text              Not Null,
        SourceCode    Varchar(100)      Null,  -- 程序源码的下载路径
        Source        Varchar(50)       Not Null Default 'TraceFact',   -- 文章出处
        SrcUrl        Varchar(150)      Null,  -- 文章出处的URL
        PostDate      DateTime          Not Null Default GetDate(),
        ViewCount     Int               Not Null Default 0,
        ClassId       Int               Not Null   -- 外键包含的字段,文章类别
    
        Constraint pk_Article Primary Key(Id)   -- 建立主键
    )

    可以看到,在这里我使用了 Check 约束,以确保文章类型只能为 0,1,2。这里,我想说的是Check 约束的命名规则:尽管Check约束是针对字段的,但在同一数据库中,却不能有同名的Check约束。所以,建议使用 ck_ + 表名 + 字段名 来命名它,比如这个范例脚本中的 ck_ArticleType。

    除此以外,我还使用了Unique约束,以确保文章标题的唯一性。由于这是我的博客文章表,不应该出现重复的题目,这样可以避免在使用 Insert 语句时插入重复值。类似于Check约束,这里的命名规则是:uq_ + 表名 + 字段名。

    主键的命名

    按照SQL Server 的默认规范(使用企业管理器创建主键时默认产生的主键名),主键的命名为 pk_TableName。主键是针对一个表的,而不是针对一个字段的,大家有时候在企业管理器中会见到一个表的两个字段前面都会有钥匙的图标(比如SQL Server 2000自带的NorthWind范例数据库的EmployeeTerritories表),就会误以为主键是针对字段的,即是说一个表上有两个主键,其实错了,只有一个主键,但包含了两个字段,这就是常说的复合主键。为了有个更生动的认识,看下建立复合主键的SQL语句,以上面说到的多对多连接表StudentCourse为例:

    Alter Table StudentCourse
    Add Constraint pk_StudentCourse Primary key(StudentId, CourseId)

    可见,对于主键pk_StudentCourse,包含了两个字段StudentId 和 CourseId。

    外键的命名

    外键的命名为 fk_外键所在的表名_外键引用的表名。因为外键所在的表为从表,所以上式可以写为 fk_从表名_主表名

    外键包含的字段的命名,外键包含的字段和外键是完全不同的概念。外键包含字段的命名,建议为:外键所在的表名 + Id。

    考虑这样一个关系,表Hotel,字段Id, Name, CityId。表City,字段Id,Name。因为一个城市可能有好多家酒店,所以是一个一对多的关系,City是主表(1方),Hotel是从表(多方)。在Hotel表中,CityId是做为外键使用。

    在实现外键的时候我们可以这样写:

    Alter Table HotelInfo
    Add Constraint fk_HotelInfo_City Foreign Key (CityID) References City(ID)
    On Delete No Action On update No Action

    很明显,fk_HotelInfo_City是外键的名字,CityId是外键包含的字段的名字。

    NOTE:在创建数据库表的时候,一般需要写成三个SQL脚本文件。第一个文件仅包含所有的创建表的SQL语句,即Create Table 语句。第二个文件包含删除关系和表的语句,其中,所有删除关系的语句,即Drop Constraint 语句集中在这个文件的上半部分,所有删除表的语句,Drop Table语句,集中在这个文件的下半部分。第三个文件包含建立表之间关系的语句。这种做法会在你移植数据库的时候产生较大的便利,原因我就不解释了,您一试便知。

    而对于多对多关系中解析表的外键包含的字段,顺理往下推,我们可以这样写(再次回到学生选课的多对多例子中):

    建立解析表StudentCourse与Student表的外键关系:

    Alter Table StudentCourse
    Add Constraint fk_StudentCourse_Student Foreign Key (StudentId) References Student (Id)
    On Delete No Action On Update No Action

    建立解析表StudentCourse与Course 表的外键关系:

    Alter Table StudentCourse
    Add Constraint fk_StudentCourse_Course Foreign Key (CourseId) References Course(Id)
    On Delete No Action On Update No Action

    触发器的命名

    由三部分构成:

    1. 前缀(tr),描述了数据库对象的类型。
    2. 基本部分,描述触发器所加的表。
    3. 后缀(_I、_U、_D),显示了修改语句(Insert, Update及Delete)

    存储过程的命名

    大家知道,系统存储过程的前缀是 sp_,为了避免将用户存储过程与系统存储过程混淆,这里我推荐大家使用 pr 作为自己定义的存储过程的命名。

    同时,命名的规则是:采用自解释型的命名,比如:prGetItemById。

    这里,有个有意思的地方值得深思。我们按上面规则命名存储过程的时候,可以用两种方式:

    1. 动词放前面,名词放后面。
    2. 名词放前面,动词放后面。

    我个人推荐使用方式2,现在说说原因:

    以NorthWind 为例,假如对于 Employees 表你有4个存储过程,分别命名为:prEmployeeInsert、prEmployeeUpdate、prEmployeeDelById、prEmployeeGetById

    同时对于 Products 表你有类似的4个存储过程,分别命名为:prProductInsert、prProductUpdate、prProductDelById、prProductGetById

    这时,你用企业管理器查看时,会发现存储过程像下面这样整整齐齐的排列:

    prEmployeeDelById
    prEmployeeGetById
    prEmployeeInsert
    prEmployeeUpdate
    prProductDelById
    prProductGetById
    prProductInsert
    prProductUpdate

    很容易就会发现,当你的存储过程越多时,这种命名方法的优势就越明显。

    存储过程中参数的命名

    存储过程中的入口参数,我建议与其对应的字段名相同,这里,假设要写一个更新Northwind数据库Employees表的存储过程(做了简化),可以这么写:

    Create Procedure prEmployeeUpdateById
        @EmployeeId       Int,
        @LastName     NVarchar(20),
        @FirstName    NVarchar(10)
    As
        Update Employees Set
           LastName = @LastName,
           FirstName = @FirstName
        Where
           EmployeeId = @EmployeeId
    
        If @@error <> 0 or @@RowCount = 0
           Raiserror 16001 ‘更新用户失败’

    总结

    在这篇文章中,我首先提出了开发人员对数据库对象命名不够重视的问题,随后列出了一张数据对象命名的简表。

    接着我按照 表、字段、主键、外键、触发器、存储过程的顺序,详细讲述了数据库对象命名的规则。

    其间,我还穿插着讲述了在数据库开发中常见的一些问题,包括建表时需要注意的问题,以及在管理存储过程时可以采取的技巧。



    展开全文
  • WPF中使用流文档灵活地显示内容

    千次阅读 2008-12-07 21:46:00
    WPF中使用流文档灵活地显示内容 WPF中使用流文档灵活地显示内容 by: Markus Egger form: http://msdn.microsoft.com/msdnmag/issues/07/08/wpf/default.as

    WPF中使用流文档灵活地显示内容

                              WPF中使用流文档灵活地显示内容
                                by: Markus Egger
                                form: http://msdn.microsoft.com/msdnmag/issues/07/08/wpf/default.aspx?loc=zh

     
    Windows ® Presentation Foundation (WPF) 提供了一系列功能。事实上,功能是如此之多,以至于其中一些非常重要的功能都没有得到应有的关注。一个最好的例子就是“流文档”功能,它可让开发人员在 WPF 中本机创建文档。在《MSDN ® 杂志》2006 年 1 月期的“XPS 文档:创建 XML 文件规范文档所用的 API 初探”中,Bob Watson 让我们 详细了解 WPF 中的 XPS 文档,但“流文档”则不同。XPS(XML 文件规范)针对打印和面向页面的内容,而“流文档”则针对屏幕显示以及提供更动态和可以论证的更复杂模型。“流文档”几乎适用于与文本内容相关的所有方面,从产品说明到整本书籍。

    文本显示无疑是更重要的 UI 功能之一。在 WPF 界面中,您通常使用标签等控件来显示文本。但是在许多情形下,您需要的不只是简单地显示几个单词。流文档提供了一种更高级的方法,而它们实质上非常简单。它们通过类似 HTML 文档的格式定义文本流,但其功能更强大,并可提供明显更先进的布局选项。

    通常使用基于 XML 的标准标记语言——可扩展应用程序标记语言 (XAML) 来定义“流文档”。XAML 对于流文档特别直观,主要是因为它与 HTML 类似。以下流文档示例创建了一段文字,并只对其中几个单词应用了粗体格式:

    <FlowDocument
      xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
      xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
      <Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy dog.
    </Paragraph>
    </FlowDocument>
    

    可以看到,与 HTML 的相似性比在其他 XAML UI 中更明显。真正的元素名称是不同的,但是至少对简单的文档来说,模式非常相似。流文档一般以包含多个块的 FlowDocument 根元素开头。“块”是指流内的元素,通常是如上例所示的文本段落(当然还有其他块类型)。段落又可以包含其他元素,例如本例中的两个粗体单词。请注意,对于任何其他 XAML 文档,根元素必须包含 XAML 特定的命名空间定义,否则无法被识别。这是 XAML 特定的实现细节,与流文档无关。请注意,命名空间定义只在独立的流文档中才需要。(流文档可以是更大的 XAML UI 的一部分,在这种情况下,该 UI 的根元素中会包含命名空间定义。)

    当然,用户永远不会看到流文档的 XAML(而 HTML 源则可在浏览器中查看),这与他们无法看到任何其他 UI 元素的 XAML 一样。相反,用户看到的是文档的最终呈现。对于这个特定的示例,您可通过多种方式看到结果。或许最简单的方式是将其键入 Windows SDK 附带的实用工具 XamlPad 中(请参见图 1)。

    图 1 XamlPad 中显示的极其简单的流文档
    图 1  XamlPad 中显示的极其简单的流文档 (单击该图像获得较大视图)

    当然,这是一个非常简单的例子,文档的定义和嵌入式布局会复杂得多。流文档支持您能想到的所有格式,例如斜体、下划线、字体颜色和字体等。图 2 显示的是一个稍微高级的示例,其结果可在图 3 中看到。

    图 3 带有稍微高级格式的流文档
    图 3  带有稍微高级格式的流文档 (单击该图像获得较大视图)

    本示例显示的是带有内嵌格式的若干段落。它还提供另一类型块元素的第一个示例,即列表,毫无疑问,它包含多个列表项。请注意,每个列表项反过来也只是包含更多块元素的容器。因此,我不是简单地将文本置于一个列表项中,而是向每个列表项中添加一个段落元素。就此而论,我应该已向每个列表项或任何其他块类型添加了多个段落。这可以让您在列表的单个列表项内创建高级布局,这在 HTML 等格式中一般行不通,因为此类格式只会让简单的文本字符串流向每个列表元素。


    流文档基础知识

    至此您已了解了一些流文档的基础知识,接下来让我们回顾一下某些基础知识。如您所见,流文档是块的集合。在内部,所有块都是从 System.Windows.Documents.Block 类派生而来的 WPF 类。块又是从 ContentElement 派生而来(沿此链向上追寻几步),ContentElement 是 WPF 中专门为文档定义优化的一个相当低级别的类。此方法有些类似于您用来定义 WPF 界面的控件,它们都从 UIElement 派生而来。两者的继承树在概念上很相似,但并不完全相同。这意味着 WPF 控件和块不能直接组合。例如,一个按钮的标题不能设为一段文本,一个段落也不能直接包含一个按钮。这些控件和块之间存在一些细微差别,这是由于内容控件内的布局和块内的布局的运作方式截然不同这一事实所致。幸运的是,这两类 WPF 元素之间需要弥合的差异非常小。就按钮而言,它可以包含由带格式的文本构成的 TextBlock 对象;而块可以通过特殊 BlockUIContainer 块类包含任何 WPF 控件。这意味着,流文档可以包含所有类型的 WPF 元素(包括交互式用户界面、媒体和三维元素),而从另一个角度看,流文档也可是任何 WPF 用户界面的一部分,例如可以是控件内容的一个高级布局元素,也可以是一个真正的元素,例如销售点应用程序中的某一项的描述。

    可用块的列表理论上是可扩充的,因为开发人员可以派生他们自己的块类,然后创建他们自己的针对文档呈现引擎的增强功能。这提供了我所了解的任何其他文档呈现引擎都无法提供的自由度。但是,对一般的文档创建者公开的块数量通常有限。图 4 显示了最重要的块类型的列表。

    当使用 XAML 创建 WPF 流文档时,您事实上只要实例化某些类型。请看下面的 XAML 代码段(从此处起,我将省略命名空间定义,以让示例尽量简单):

    <FlowDocument>
      <Paragraph>Hello World!</Paragraph>
    </FlowDocument>
    

    这会实例化一个 FlowDocument 类和 Paragraph 类(其文本设为“Hello World!”)。该段落被添加到 FlowDocument 的块集合中。请注意,对于所有 XAML 而言,元素名称都区分大小写,并且精确映射到作为 WPF 一部分而提供的类。您也可通过编程方式创建相同文档,如下所示:

    FlowDocument doc = new FlowDocument();
    Paragraph para = new Paragraph();
    para.Inlines.Add(“Hello World!”);
    doc.Blocks.Add(para);
    

    当然,这远不及 XAML 提供的声明性方法那么直观,因此编程的方法只在特殊情形下采用。(当我需要创建一个格式丰富的报告,结果要更像一份真实的文档,而非通过许多报告引擎创建的表格形式的输出时,有时会使用此方法。)

    在许多情形下,段落本身带有格式丰富的内容,这也是通过实例化类实现的,如下所示:

    <Paragraph>Hello <Bold>World!</Bold></Paragraph>
    

    在本例中,该段落包含两个文本段——“Hello”(使用默认格式)和“World!”(粗体)。这比较有趣,因为这表示此 XAML 不只是实例化一个段落,并将其文本设为一个简单的字符串;相反,它创建了含有两个子段的一个段落,每个子段包含不同格式的文本。在 WPF 中,这些段称为内嵌元素。就如一个流文档可以包含多个不同类型的块一样,段落也可以包含各种类型的内嵌元素。内嵌元素有多种变体。有些内嵌元素就是所谓的 Span,它们代表应用了特定格式选项的文本段。此例中的 Bold 元素是 Span 的一个特殊情形,其默认字体粗细设为粗体。内嵌元素的另一种类型是 Run,它是带有默认格式的文本段。因此,上面的 XAML 其实只是下例的简化:

    <Paragraph>
      <Run>Hello </Run>
      <Bold>World!</Bold>
    </Paragraph>
    

    当然,它要方便得多,您不必使用 XAML 定义每个内嵌元素,但是如果您要以编程方式创建相同示例,了解内嵌元素的概念就非常重要了,因为它们不可以在代码中省略。以下是前面两个 XAML 示例的对等代码段:

    Paragraph para = new Paragraph();
    para.Inlines.Add(new Run(“Hello “));
    Bold b = new Bold();
    b.Inlines.Add(“World!”);
    para.Inlines.Add(b);
    

    Bold 是 Span 的特殊版本,其默认字体粗细设为粗体;Bold 类型由 Span 子类化而来,并且会覆盖 FontWeight 属性。类似特殊的 Span 还有 Italic 和 Underline。不过,这些特殊的 Span 并不是必不可少的,因为您也可以使用默认的 Span,并设置相应属性:

    <Paragraph>Hello <Span FontWeight=”Bold”>World!</Span></Paragraph>
    

    当然,通过将某一文本段包到粗体或斜体标记中,来直接指定诸如粗体和斜体等属性的功能非常方便和直观,因此通常更多的是使用 <Bold>,而不是 <Span FontWeight="Bold">。不过,<Span> 元素还是非常有用的,因为有许多属性都要设为粗体以外的属性,而且那其中的大多数格式选项都没有单独的 Span 类型。事实上,许多非常常见的格式选项没有特殊的 Span。一个典型的示例就是设置字体。与 HTML 不同,流文档没有 <Font> 元素。相反,字体按如下方式设置:

    <Paragraph>Hello <Span FontFamily=”Comic Sans MS” FontSize=”24”>
        World!</Span></Paragraph>
    

    诸如 FontFamily 等许多属性都可以始终在所有流文档类中找到。例如,若要设置一个完整段落而非只是一个内嵌元素的字体,您不使用 Span 即可做到:

    <Paragraph FontFamily=”Comic Sans MS” FontSize=”24”>Hello World!</Paragraph>
    

    还有 Span 和 Run 之外的一些内嵌元素。下面就是其他一些更有趣的内嵌元素:

    Figure  Figure 是有些不寻常的内嵌元素,因为它们包含块。因此,从某种意义上讲,Figure 几乎就像流文档内的迷你流文档。Figure 经常用于高级布局功能,例如段落中被普通文本流包围的图像。

    Floater  Floater 是轻型的图形。它们不支持任何图形放置选项,但是如果您需要的只是除标准段落对齐之外还能做些简单对齐的功能,Floater 会比较有用。

    LineBreak  LineBreak 元素的作用与其名称所指的意义完全相同:它们会在段落内引入换行符。

    InlineUIContainer  InlineUIContainer 是 BlockUIContainer 的内嵌元素等同项。如果您需要将任何类型的 WPF 控件与您其他的内嵌元素组合使用(例如让一个按钮在一个段落文本内移动),InlineUIContainer 正是您所需要的。

    Figure 始终用于流文档中(LineBreak 也是如此,不过它们几乎不需要详细讨论)。以下示例使用一个图形,将一个图像显示为一个更大流文档的一部分:

    <Paragraph>
      <Figure Width=”200”>
        <BlockUIContainer>
          <Image Source=”Pictures/Humpback Whale.jpg” />
        </BlockUIContainer>
        <Paragraph Foreground=”Blue” FontFamily=”Consolas”>
            The Whale</Paragraph>
      </Figure>
      The quick brown fox jumps over the lazy dog. The quick brown...
    </Paragraph>
    

    请注意,WPF 流文档中没有 Image 块。相反,图像以标准的 WPF Image 控件内嵌为 BlockUIContainer。(相同的方法也用于流文档内诸如视频或交互式三维模型等内容)。图 5 显示了与此类似的一个文档的呈现。

    图 5 文本环绕图片和标题
    图 5  文本环绕图片和标题 (单击该图像获得较大视图)

    查看流文档

    现在,您已了解如何创建一些简单的流文档以及如何在 XamlPad 中查看它们。而目前我所忽略的是该如何在自然状态下查看流文档。毕竟,您不会期望用户打开 XamlPad,然后粘贴文档的 XAML。查看 XAML 流文档的一种方法是将其另存为一个扩展名为 .xaml 的文件,然后在 Windows 资源管理器中双击它。这会启动与 XAML 文件相关联的默认应用程序(通常是 Internet Explorer®),从而显示该文档。结果如图 6 所示。

    图 6 在 Internet Explorer 中显示的 XAML 流文档
    图 6  在 Internet Explorer 中显示的 XAML 流文档 (单击该图像获得较大视图)

    Internet Explorer(及其他浏览器)可以显示 XAML 内容这一事实特别有趣,因为这是将流文档作为您的 Web 应用程序一部分显示的一张票证。换句话说,如果您将 XAML 流文档上传到您的 Web 服务器,而有人浏览到了该文件,他就会看到类似于图 6 的效果(假设该用户已安装 Microsoft® .NET Framework 3.0)。当然,这也是动态运作的。如果您的 ASP.NET Web 应用程序(或任何其他服务器端技术)动态生成了一个 XAML 流文档,并将其作为输出返回(假设内容类型已适当设为“application/xaml+xml”),用户就会看到作为您应用程序一部分的流文档,这在许多情形下必然相当有用。图 7 显示了一个简单的生成流文档的 ASP.NET 页面。


    显示流文档

    您可能已经注意到,每当显示流文档时(无论是在浏览器中还是在 XamlPad 中),显示的似乎不只是文档本身,还会显示其他少量内容。特别是,文档底部会呈现一些控件。如图 8 所示,流文档默认会通过 FlowDocumentReader 控件呈现,它提供了一组标准功能,例如缩放、分页、不同视图模式切换,甚至查找功能。事实上,流文档需要由一些能够显示它们的某类控件承载。流文档的默认查看器是 FlowDocumentReader 控件,除非您明确使用其他控件,否则该控件会自动实例化。WPF 目前提供三个不同的控件用于查看流文档:

    图 8 FlowDocumentReader 控件中的控件按钮
    图 8  FlowDocumentReader 控件中的控件按钮 (单击该图像获得较大视图)

    FlowDocumentScrollViewer  此控件使用一个滚动条以连续的流显示文档,类似网页或 Microsoft Word 中的“Web 版式”。图 9 显示的是滚动查看器中的文档。

    图 9 使用 FlowDocumentScrollViewer 控件
    图 9  使用 FlowDocumentScrollViewer 控件 (单击该图像获得较大视图)

    FlowDocumentPageViewer  此控件以单独的页面显示流文档,让页面翻转而非滚动。这与 Word 中的“阅读版式”类似。图 10 显示的是页面查看器。在这里,图 9 中的文档使用 FlowDocumentPageViewer 控件呈现,滚动条被分页机制取代。这种简单的流布局方法已被一种更高级、多列的分页布局所取代。

    图 10 使用 FlowDocumentPageViewer 控件
    图 10  使用 FlowDocumentPageViewer 控件 (单击该图像获得较大视图)

    FlowDocumentReader  此控件组合了滚动查看器和页面查看器,让用户可以在两种方法之间切换。这是用于流文档的默认控件,而且对于以显示复杂文本为特色的应用程序通常是一个不错的选择。在图 11 中,图 9 和图 10 中显示的同一文档通过 FlowDocumentReader 呈现,它将滚动查看器和页面查看器两种方法结合在一起。此外,它还启用了其他控件中默认隐藏的搜索功能(其他查看器的确支持查找功能,通过执行 ApplicationCommands.Find 命令或从键盘上按 Ctrl+F 可实现该功能)。读取器控件还支持多页视图,这稍微改变了基于页面的呈现,以及列和图的呈现方式。

    图 11 使用 FlowDocumentReader 控件
    图 11  使用 FlowDocumentReader 控件 (单击该图像获得较大视图)

    虽然 FlowDocumentReader 几乎对所有基本使用情形都很有吸引力,但选择怎样的控件还需视您的情况而定。它用途广泛且功能强大,并支持分页布局,这在许多情形下是比滚动更高级的功能。关于该主题的更详细讨论不在本文探讨范围之内,但事实证明,滚动及重合等相关效果是人们较之数字化文本更喜欢打印文本的主要原因之一。分页方法在许多情况下更为自然,有助于让数字化阅读被更普遍接受。

    那么您如何定义要使用哪个控件呢?一个简单但相当强力的方法是将想要的控件添加到文档的 XAML 中:

    <FlowDocumentScrollViewer 
      xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’
      xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
      <FlowDocument>
        <Paragraph>The quick <Bold>brown fox</Bold> jumps over the lazy
            dog.</Paragraph>
      </FlowDocument>
    </FlowDocumentScrollViewer>
    

    在本例中,文档根已被设为一个 FlowDocumentScrollViewer 标记。也就是说,您不再只是定义一个单纯的文档而已。相反,您在定义一个完整的 XAML 界面,而它碰巧使用滚动查看器作为其根。滚动查看器的内容是最开始示例中的流文档。(请注意,命名空间定义现在使用滚动查看器标记,而非流文档标记)。图 9 到图 11 是使用此方法创建的,不同的查看器控件用作根元素。

    我为何把这称为强力方法呢?这是因为,从结构角度看,将用户界面定义与其实际数据相混合会导致一些问题。而更理想的状况是将文档与其界面分开。将读取器与文档混合在一起有点像创建一个 SQL Server™ 表,并出于某种原因定义该表只能在 Windows Forms DataGrid 中显示。有若干方法可让文档与 UI 定义分离。如果想使用上文所示的 ASP.NET 方法将流文档作为 Web 应用程序的一部分显示,您可使用所需的查看器控件定义 ASP.NET 页面,然后只要使用标准 ASP.NET 代码合并到实际内容(单独存储,可能在数据库中)即可。

    另一方面,在一个典型的 WPF 应用程序中,您可以只要使用标准 WPF、Windows 和 XAML 浏览器应用程序 (XBAP) 方法来定义您的用户界面,然后动态加载您的文档即可。图 12 显示的是使用我文章中的一个虚构库的一个简单示例,这些文章显示在左上角的一个列表框中。用户从列表中选择一篇文章时,该文档会自动加载到占用大部分窗体的 Flow Document Reader 控件。请注意,诸如 alpha 值混合处理等标准 WPF 技术在此设置中也能使用。您会注意到,实际的流文档是半透明的,背景中我的照片也在闪烁。另外也请注意,应用程序使用了一个列表框、图像,一个标签和一个 FlowDocumentReader 控件来创建虚构文章的库。

    图 12 使用列表框、图像、标签和 FlowDocumentReader 控件
    图 12  使用列表框、图像、标签和 FlowDocumentReader 控件 (单击该图像获得较大视图)

    这个例子最棘手的地方是将实际文档加载到查看器控件中。这通过 System.Windows.Markup.XamlReader 类实现,它允许动态加载任何 XAML 内容,包括但不限于流文档。以下是我绑定到列表框选定更改事件的一行代码:

    documentReader.Document = 
        (FlowDocument)XamlReader.Load(
    File.OpenRead(fileName));
    

    Load 方法会返回一个对象,因为 XAML 文件中的根元素可以代表许多不同类型。在我的例子中,我知道返回值为 FlowDocument,因此我只要执行一个转换,并将该文档指定给 FlowDocumentReader 控件的 Document 属性即可(此例中,我将控件实例命名为 documentReader)。请记住,这只是个例子。生产品质的代码此处当然还需要一些错误处理。

    请注意,您了解的关于 WPF 的所有东西都适用于本例。例如,读取器控件只是支持样式的标准 WPF 控件。也就是说,您可以完全更改所有 UI 元素的外观,例如缩放栏、视图模式切换或分页控件。(您的控制能力受到限制的唯一元素是搜索框,虽然如果您不喜欢它,就根本不必用它。)

    此外,我的例子显示的是基于 Windows 的应用程序,相同的应用程序也可以作为 XBAP 部署,并在 Web 浏览器内运行(当然,我们还是假设用户已安装了 .NET Framework 3.0)。请注意,Microsoft Silverlight™(原代号为“WPF/E”)是不够的,因为 Silverlight 只支持 WPF 的子集,且并不支持流文档。


    创建流文档

    如何编写流文档?当然,开发人员始终可以使用诸如 XamlPad 等低级工具来编写流文档。但是,在现实环境下,这不大可能。通常,流文档是使用 WYSIWYG 编辑器或通过从现有文档格式进行的内容转换来创建的。由于流文档可以使用 XAML 定义,因此转换现有 XML 内容特别简单。但也可以转换 HTML 和 Word 文档,而无需付出过大的精力(尽管需要编码,因为迄今为止尚未出现现成工具)。

    对于 WYSIWYG 编辑,WPF 提供了一个现成的控件。WPF RichTextBox 控件可以本机编辑 XAML 流文档。该控件名称让人误以为它是专门针对 RTF 格式。尽管这个控件也支持 RTF,但实际上它主要用于流文档。事实上,该控件实际上会反映流文档查看控件,只不过它也支持编辑。有些人甚至会说,RichTextBox 控件应该被视为显示流文档的另一种方式。

    将下列示例键入 XamlPad 中,以查看运行中的 RichTextBox 控件:

    <RichTextBox
      xmlns=’http://schemas.microsoft.com/
          winfx/2006/xaml/presentation’
      xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’>
      <FlowDocument>
        <Paragraph>
          The quick brown fox jumps over the lazy dog.
        </Paragraph>
      </FlowDocument>
    </RichTextBox>
    

    恰如读取器控件一样,RichTextBox 也有一个 Document 属性,您可以自动以此会话中的流文档填充其值。这实际上会创建一个与 FlowDocumentScrollViewer 控件看起来很相似的 UI,只不过其中的文本可以编辑。请注意,此文本框控件始终以滚动方式处理流文档。在分页或多列模式下,无法在 RichTextBox 中编辑流文档。不过,编辑操作的结果是一个标准流文档,该文档可以使用您已看到的任何一种查看器机制显示,其中包括多列和分页模式。

    关于 RichTextBox,值得一提的其中一项功能是集成的拼写检查。您可以按如下所示启用该功能:

    <RichTextBox SpellCheck.IsEnabled=”true”>
      <FlowDocument>...</FlowDocument>
    </RichTextBox>
    

    图 13 显示了运行中的拼写检查程序。

    图 13 带有拼写检查功能的 RichTextBox 控件
    图 13  带有拼写检查功能的 RichTextBox 控件 (单击该图像获得较大视图)

    使用此控件唯一复杂的地方是加载与保存。在许多情形下,您可能不会像在之前的例子中那样,将 RichTextBox 内容编码到 UI XAML 中,而是要动态加载和保存文档。RichTextBox 中文本的加载操作与为查看器控件加载流文档相同(见上文)。保存文档本质上则完全相反:您要先拿到文档对象,然后将其序列化回 XAML,如下所示:

       System.Windows.Markup.XamlWriter.
       Save(richTextBox.Document)
    

    这会将 XAML 作为一个字符串返回,然后您可以将其存储到文件或数据库中,或者使用您能想到的任何其他方式。

    RichTextBox 非常方便,不过在这里还是要提醒几句话。虽然流文档代表了可用于呈现屏幕文档的最复杂的技术,但 RichTextBox 控件却一点也不复杂。它是编辑小型文档和文本段的极佳选择,但是您不会用它来编写书籍、杂志或营销手册。对于这些长格式,它的呈现过于简单,因为它不支持除滚动布局之外的其他任何布局(也就是说,还没有一种很好的可视方式可用于创建我稍后将谈到的高级布局)。同样,用于保存文档的方法也经常不尽人意。XmlWriter 类只是使用实时的内存中文档,并将其转换为 XAML,但遗憾的是,对于大规模的流文档操作非常重要的许多概念(例如样式),它并未注意。结果,尽管 XAML 忠实地保存了文档的外观,但文档看起来往往不太清爽,并且很大。RichTextBox 控件当然还是很有用的,但是别指望将它作为屏幕内容的桌面出版解决方案(虽然这类应用程序非常急需)。


    探究布局可能性

    至此您已了解了如何编写和查看流文档,接着让我们回到文档本身,看看更多的功能。流文档非常复杂,探究所有可用功能超出了本文范围,不过我想再讨论几项功能。

    其中一项一直让我着迷的功能是“最佳段落”。启用该功能后,可以在指定段落内尽可能平均地分布空白,从而带来显著改进的阅读体验。“最佳段落”特别适合与另一项内置功能“断字”搭配使用,该功能(居然)会执行动态整个流文档或者个别段落的断字。

    启用最佳段落和断字功能是项非常简单的操作:

    <FlowDocument IsOptimalParagraphEnabled=”true” 
        IsHyphenationEnabled=”true”>
    

    图 14 显示的是相同的文档,只是呈现时启用或禁用了这些功能。两个版本间的区别非常细微,但是非常重要。请注意,左边的版本看起来更平和,主要因为词与词之间的空白分布得更平均,且从整体上减少了。特别是在屏幕上阅读大量文本时,这个看起来细小的区别会变得极为重要。

    图 14 最佳段落和断字
    图 14  最佳段落和断字 (单击该图像获得较大视图)

    如您所见,FlowDocumentReader 控件采取多列的方法呈现文本。这是另一项非常重要的可读性功能,因为人们不喜欢读跨越整个宽屏显示页面宽度的一行行文字。实际列宽因各种因素会有所不同,例如用于内容显示的可用总宽度、缩放系数和定义的列宽等。流文档的默认列宽为字体大小的 20 倍,默认字体大小约为 300 个与设备无关的像素(3 1/8 英寸的精确尺寸显示)。您可以很轻松地覆盖此默认设置:

    <FlowDocument ColumnWidth=”400”>
    

    这会产生宽度约 400 像素的列。不过,还有其他一些因素会影响实际宽度。举例来说,如果缩放比例是 50%,那么实际列宽就只有 200 像素。另外,到目前为止,列宽更多地会被看作最小列宽。这意味着,如果可用总宽度为 900 像素,要呈现结果包含两列,并且要充分填满这整个 900 像素的话,就要让每列的宽度都超过定义的 400 像素。通常都需要这样,因为它会让呈现结果看起来非常美观。不过,如果您不想执行该行为,而只希望列宽实际就是 400 像素的话,可以确保列宽不是灵活可变的:

    <FlowDocument ColumnWidth=”400” IsColumnWidthFlexible=”false”>
    

    现在,所有列都正好是 400 像素(100% 缩放),剩余空间就让它显示为空白。

    另一个您可能想尝试的与列相关的设置是列之间的空隙。这可以通过 ColumnGap 属性调整(此设置也是基于与设备无关的像素数):

    <FlowDocument ColumnGap=”25”>
    

    其中一个相关的设置是列规则,它允许在列之间定义一个可视元素。请看此例(其结果见图 15):

    图 15 列之间采用简单规则的流文档
    图 15  列之间采用简单规则的流文档 (单击该图像获得较大视图)

    <FlowDocument ColumnRuleWidth=”5” ColumnRuleBrush=”Red”>
    

    当然,在许多出版物中,文档并不只是采用简单的列布局。通常还存在从一般流中提取出来的其他元素。您已见过这样的例子,例如将图像置于文档中。图 12 显示了图形设计师常用的一种排列方式。此图像位于两列之间,周围环绕文字,图像方方正正地位于内容中间,并没有影响任何一列的文字布局。这是一种常见的布局选择,只是还不能用于流文档之前我所了解的动态屏幕阅读环境。

    创建此类布局的关键是图形块,它允许定义不与文档其余部分那样布局的内容。将图像置于图形标记内部就是一例,但图形还有许多其他用途。例如,您可以使用图形来定义横跨整个文档宽度的标题:

    <Paragraph>
      <Figure HorizontalAnchor=”ContentLeft” VerticalAnchor=”ContentTop” 
              Width=”1Content”>
        <Paragraph FontSize=”36” FontWeight=”Bold”>Go With 
            The Flow</Paragraph>
      </Figure>
      Windows Presentation Foundation in Windows Vista provides a great set
        of features.
    </Paragraph>
    

    在本代码中,图形包含另一个段落,即用作标题的文本。请注意,这里有一些您可用来创建高级、灵活文档的便捷属性。例如,看一下图形的宽度。我没有将宽度设为特定像素数,而是将其设为内容的确切宽度,这会根据整个内容的宽度自动调整图形宽度。

    请看图 16。其中,您会注意到标题(通过图形放置)设为横跨整个内容宽度,这就将所有四列的位置都向下推移了。该图像从垂直和水平方向看都定位于页面中央。

    图 16 标题横跨四列
    图 16  标题横跨四列 (单击该图像获得较大视图)

    请注意,其宽度与内容相关的图形不必始终与内容一样宽。以下例来说,图形宽度设为内容宽度的 75%:

    <Figure Width=”0.75Content”>
    

    宽度也可与其他项相关,例如列宽。下例图形始终是两列宽(除非只显示一列,那样宽度就会减为一列):

    <Figure Width=”2Column”>
    

    当然,图形高度可通过类似方式定义(虽然图形通常是随着内容纵向变化)。

    另一重要方面是图形的位置。在代码段中,它设为横向定位为靠左,纵向定位为靠上。也就是说,图形会出现在当前内容页面的左上角,而无论其实际如何定义。然而在本示例中,图形被定义为文档的第一个元素,但即使该标题之前已有段落,它也会由于这些设置而被上移和左移。图 12图 16 中的照片已按类似方式,将其横向定位为“PageCenter”,在列之间移动。(所有这些设置的可用属性值都可以在 WPF 文档中找到)。

    您可能已经注意到,本文涉及了大量手动编码。例如,每当需要改变字体时,您都要将该信息添加到块或内嵌元素中。到目前为止,这还不是一个大问题,因为大部分示例都很小。但是,如果有一本每 50 页为一章的书,您要改变每一段的字体,每次都手动来改的话,无疑会很繁重。幸运的是,现在有了一个更好的办法:如 WPF 中的其他任何内容一样,流文档支持样式。样式可被定义为实际流文档中指定名称的资源。以下是定义字体信息的样式:

    <FlowDocument>
      <FlowDocument.Resources>
        <Style x:Key=”MyStyle”>
          <Setter Property=”TextElement.FontSize” Value=”12” />
          <Setter Property=”TextElement.FontFamily” Value=”Bodoni MT” />
        </Style>
      <FlowDocument.Resources>
      ...
    </FlowDocument>
    

    然后,该样式会通过以下方式应用到段落(和其他元素):

    <Paragraph Style=”{StaticResource MyStyle}”>The quick... </Paragraph>
    

    由于流文档的特性,样式特别常用。建议您对于最简单情形之外的任何情形,都使用样式来定义大部分格式选项,而不是通过个别内嵌元素的属性。样式可让您的文档保持紧凑,而且更易维护。


    总结

    希望本文不只让您获得对流文档及其功能的基本了解,而且也激发起您的兴趣。还有许多更高级的功能,包括查看器控件的复杂样式、子类化和延伸文档、块和内嵌元素、数字权限管理、文本和墨迹注释功能以及高级字体格式等,绝对值得您深入研究。

    展开全文
  • 一个最好的例子就是“流文档”功能,它可开发人员在 WPF 中本机创建文档。“流文档”针对屏幕显示以及提供更动态和可以论证的更复杂模型。“流文档”几乎适用于与文本内容相关的所有方面,从产品说明到整本书籍。...

        Windows® Presentation Foundation (WPF) 提供了一系列功能。事实上,功能是如此之多,以至于其中一些非常重要的功能都没有得到应有的关注。一个最好的例子就是流文档功能,它可让开发人员在 WPF 中本机创建文档。流文档针对屏幕显示以及提供更动态和可以论证的更复杂模型。流文档几乎适用于与文本内容相关的所有方面,从产品说明到整本书籍。

       
    文本显示无疑是更重要的 UI 功能之一。在 WPF 界面中,您通常使用标签等控件来显示文本。但是在许多情形下,您需要的不只是简单地显示几个单词。流文档提供了一种更高级的方法,而它们实质上非常简单。 它们通过类似 HTML 文档的格式定义文本流,但其功能更强大,并可提供明显更先进的布局选项。

       
    通常使用基于 XML 的标准标记语言——可扩展应用程序标记语言 (XAML) 来定义流文档XAML 对于流文档特别直观,主要是因为它与 HTML 类似。以下流文档示例创建了一段文字,并只对其中几个单词应用了粗体格式:

        可以看到,与 HTML 的相似性比在其他 XAML UI 中更明显。真正的元素名称是不同的,但是至少对简单的文档来说,模式非常相似。流文档一般以包含多个块的 FlowDocument 根元素开头。是指流内的元素,通常是如上例所示的文本段落(当然还有其他块类型)。段落又可以包含其他元素,例如本例中的两个粗体单词。请注意,对于任何其他 XAML 文档,根元素必须包含 XAML 特定的命名空间定义,否则无法被识别。这是 XAML 特定的实现细节,与流文档无关。请注意,命名空间定义只在独立的流文档中才需要。(流文档可以是更大的 XAML UI 的一部分,在这种情况下,该 UI 的根元素中会包含命名空间定义。)
       
    当然,用户永远不会看到流文档的 XAML(而 HTML 源则可在浏览器中查看),这与他们无法看到任何其他 UI 元素的 XAML 一样。相反,用户看到的是文档的最终呈现。对于这个特定的示例,您可通过多种方式看到结果。或许最简单的方式是将其键入 Windows SDK 附带的实用工具 XamlPad 中(请参见图 1)。

     

     

     

     

     

     

     

     

     

     

     

     

    请注意,WPF 流文档中没有 Image 块。相反,图像以标准的 WPF Image 控件内嵌为 BlockUIContainer。(相同的方法也用于流文档内诸如视频或交互式三维模型等内容)。图 5 显示了与此类似的一个文档的呈现。 

     

    查看流文档

       
    现在,您已了解如何创建一些简单的流文档以及如何在 XamlPad 中查看它们。而目前我所忽略的是该如何在自然状态下查看流文档。毕竟,您不会期望用户打开 XamlPad,然后粘贴文档的 XAML。查看 XAML 流文档的一种方法是将其另存为一个扩展名为 .xaml 的文件,然后在 Windows 资源管理器中双击它。这会启动与 XAML 文件相关联的默认应用程序(通常是 Internet Explorer®),从而显示该文档。结果如图6所示。 

     

    显示流文档

       
    您可能已经注意到,每当显示流文档时(无论是在浏览器中还是在 XamlPad 中),显示的似乎不只是文档本身,还会显示其他少量内容。特别是,文档底部会呈现一些控件。如图 8 所示,流文档默认会通过 FlowDocumentReader 控件呈现,它提供了一组标准功能,例如缩放、分页、不同视图模式切换,甚至查找功能。事实上,流文档需要由一些能够显示它们的某类控件承载。流文档的默认 查看器是 FlowDocumentReader 控件,除非您明确使用其他控件,否则该控件会自动实例化。WPF 目前提供三个不同的控件用于查看流文档: 

    8 FlowDocumentReader 控件中的控件按钮 (单击该图像获得较大视图)
        FlowDocumentScrollViewer
    此控件使用一个滚动条以连续的流显示文档,类似网页或 Microsoft Word 中的“Web 版式。图 9 显示的是滚动查看器中的文档。 

    9 使用 FlowDocumentScrollViewer 控件 (单击该图像获得较大视图)
        FlowDocumentPageViewer
    此控件以单独的页面显示流文档,让页面翻转而非滚动。这与 Word 中的阅读版式类似。图 10 显示的是页面查看器。在这里,图 9 中的文档使用 FlowDocumentPageViewer 控件呈现,滚动条被分页机制取代。这种简单的流布局方法已被一种更高级、多列的分页布局所取代。 

    10 使用 FlowDocumentPageViewer 控件 (单击该图像获得较大视图)
        FlowDocumentReader
    此控件组合了滚动查看器和页面查看器,让用户可以在两种方法之间切换。这是用于流文档的默认控件,而且对于以显示复杂文本为特色的应用程序通常是一个不错 的选择。在图 11 中,图 9 和图 10 中显示的同一文档通过 FlowDocumentReader 呈现,它将滚动查看器和页面查看器两种方法结合在一起。此外,它还启用了其他控件中默认隐藏的搜索功能(其他查看器的确支持查找功能,通过执行 ApplicationCommands.Find 命令或从键盘上按 Ctrl+F 可实现该功能)。读取器控件还支持多页视图,这稍微改变了基于页面的呈现,以及列和图的呈现方式。 

     

        在本例中,文档根已被设为一个 FlowDocumentScrollViewer 标记。也就是说,您不再只是定义一个单纯的文档而已。相反,您在定义一个完整的 XAML 界面,而它碰巧使用滚动查看器作为其根。滚动查看器的内容是最开始示例中的流文档。(请注意,命名空间定义现在使用滚动查看器标记,而非流文档标记)。图 9 到图 11 是使用此方法创建的,不同的查看器控件用作根元素。

       
    我为何把这称为强力方法呢?这是因为,从结构角度看,将用户界面定义与其实际数据相混合会导致一些问题。而更理想的状况是将文档与其界面分开。将读取器与 文档混合在一起有点像创建一个 SQL Server™ 表,并出于某种原因定义该表只能在 Windows Forms DataGrid 中显示。有若干方法可让文档与 UI 定义分离。如果想使用上文所示的 ASP.NET 方法将流文档作为 Web 应用程序的一部分显示,您可使用所需的查看器控件定义 ASP.NET 页面,然后只要使用标准 ASP.NET 代码合并到实际内容(单独存储,可能在数据库中)即可。

       
    另一方面,在一个典型的 WPF 应用程序中,您可以只要使用标准 WPFWindows XAML 浏览器应用程序 (XBAP) 方法来定义您的用户界面,然后动态加载您的文档即可。图 12 显示的是使用我文章中的一个虚构库的一个简单示例,这些文章显示在左上角的一个列表框中。用户从列表中选择一篇文章时,该文档会自动加载到占用大部分窗体 Flow Document Reader 控件。请注意,诸如 alpha 值混合处理等标准 WPF 技术在此设置中也能使用。您会注意到,实际的流文档是半透明的,背景中我的照片也在闪烁。另外也请注意,应用程序使用了一个列表框、图像,一个标签和一个 FlowDocumentReader 控件来创建虚构文章的库。 

    12 使用列表框、图像、标签和 FlowDocumentReader 控件 (单击该图像获得较大视图)
    这个例子最棘手的地方是将实际文档加载到查看器控件中。这通过 System.Windows.Markup.XamlReader 类实现,它允许动态加载任何 XAML 内容,包括但不限于流文档。以下是我绑定到列表框选定更改事件的一行代码:

        Load 方法会返回一个对象,因为 XAML 文件中的根元素可以代表许多不同类型。在我的例子中,我知道返回值为 FlowDocument,因此我只要执行一个转换,并将该文档指定给 FlowDocumentReader 控件的 Document 属性即可(此例中,我将控件实例命名为 documentReader)。请记住,这只是个例子。生产品质的代码此处当然还需要一些错误处理。

       
    请注意,您了解的关于 WPF 的所有东西都适用于本例。例如,读取器控件只是支持样式的标准 WPF 控件。也就是说,您可以完全更改所有 UI 元素的外观,例如缩放栏、视图模式切换或分页控件。(您的控制能力受到限制的唯一元素是搜索框,虽然如果您不喜欢它,就根本不必用它。)

       
    此外,我的例子显示的是基于 Windows 的应用程序,相同的应用程序也可以作为 XBAP 部署,并在 Web 浏览器内运行(当然,我们还是假设用户已安装了 .NET Framework 3.0)。请注意,Microsoft Silverlight™(原代号为“WPF/E”)是不够的,因为 Silverlight 只支持 WPF 的子集,且并不支持流文档。

    创建流文档

       
    如何编写流文档?当然,开发人员始终可以使用诸如 XamlPad 等低级工具来编写流文档。但是,在现实环境下,这不大可能。通常,流文档是使用 WYSIWYG 编辑器或通过从现有文档格式进行的内容转换来创建的。由于流文档可以使用 XAML 定义,因此转换现有 XML 内容特别简单。但也可以转换 HTML Word 文档,而无需付出过大的精力(尽管需要编码,因为迄今为止尚未出现现成工具)。
     
       
    对于 WYSIWYG 编辑,WPF 提供了一个现成的控件。WPF RichTextBox 控件可以本机编辑 XAML 流文档。该控件名称让人误以为它是专门针对 RTF 格式。尽管这个控件也支持 RTF,但实际上它主要用于流文档。事实上,该控件实际上会反映流文档查看控件,只不过它也支持编辑。有些人甚至会说,RichTextBox 控件应该被视为显示流文档的另一种方式。

       
    将下列示例键入 XamlPad 中,以查看运行中的 RichTextBox 控件:

        恰如读取器控件一样,RichTextBox 也有一个 Document 属性,您可以自动以此会话中的流文档填充其值。这实际上会创建一个与 FlowDocumentScrollViewer 控件看起来很相似的 UI,只不过其中的文本可以编辑。请注意,此文本框控件始终以滚动方式处理流文档。在分页或多列模式下,无法在 RichTextBox 中编辑流文档。不过,编辑操作的结果是一个标准流文档,该文档可以使用您已看到的任何一种查看器机制显示,其中包括多列和分页模式。

       
    关于 RichTextBox,值得一提的其中一项功能是集成的拼写检查。您可以按如下所示启用该功能: 

    13 显示了运行中的拼写检查程序。 

        13 带有拼写检查功能的 RichTextBox 控件 (单击该图像获得较大视图)
    使用此控件唯一复杂的地方是加载与保存。在许多情形下,您可能不会像在之前的例子中那样,将 RichTextBox 内容编码到 UI XAML 中,而是要动态加载和保存文档。RichTextBox 中文本的加载操作与为查看器控件加载流文档相同(见上文)。保存文档本质上则完全相反:您要先拿到文档对象,然后将其序列化回 XAML,如下所示:

        这会将 XAML 作为一个字符串返回,然后您可以将其存储到文件或数据库中,或者使用您能想到的任何其他方式。

        RichTextBox
    非常方便,不过在这里还是要提醒几句话。虽然流文档代表了可用于呈现屏幕文档的最复杂的技术,但 RichTextBox 控件却一点也不复杂。它是编辑小型文档和文本段的极佳选择,但是您不会用它来编写书籍、杂志或营销手册。对于这些长格式,它的呈现过于简单,因为它不支持 除滚动布局之外的其他任何布局(也就是说,还没有一种很好的可视方式可用于创建我稍后将谈到的高级布局)。同样,用于保存文档的方法也经常不尽人意。 XmlWriter 类只是使用实时的内存中文档,并将其转换为 XAML,但遗憾的是,对于大规模的流文档操作非常重要的许多概念(例如样式),它并未注意。结果,尽管 XAML 忠实地保存了文档的外观,但文档看起来往往不太清爽,并且很大。RichTextBox 控件当然还是很有用的,但是别指望将它作为屏幕内容的桌面出版解决方案(虽然这类应用程序非常急需)。

       
    探究布局可能性

       
    至此您已了解了如何编写和查看流文档,接着让我们回到文档本身,看看更多的功能。流文档非常复杂,探究所有可用功能超出了本文范围,不过我想再讨论几项功能。

       
    其中一项一直让我着迷的功能是最佳段落。启用该功能后,可以在指定段落内尽可能平均地分布空白,从而带来显著改进的阅读体验。最佳段落特别适合与 另一项内置功能断字搭配使用,该功能(居然)会执行动态整个流文档或者个别段落的断字。

       
    启用最佳段落和断字功能是项非常简单的操作:


        14 显示的是相同的文档,只是呈现时启用或禁用了这些功能。两个版本间的区别非常细微,但是非常重要。请注意,左边的版本看起来更平和,主要因为词与词之间的 空白分布得更平均,且从整体上减少了。特别是在屏幕上阅读大量文本时,这个看起来细小的区别会变得极为重要。 

     

    14 最佳段落和断字 (单击该图像获得较大视图)
       
    如您所见,FlowDocumentReader 控件采取多列的方法呈现文本。这是另一项非常重要的可读性功能,因为人们不喜欢读跨越整个宽屏显示页面宽度的一行行文字。实际列宽因各种因素会有所不同, 例如用于内容显示的可用总宽度、缩放系数和定义的列宽等。流文档的默认列宽为字体大小的 20 倍,默认字体大小约为 300 个与设备无关的像素(3 1/8 英寸的精确尺寸显示)。您可以很轻松地覆盖此默认设置:

     

     

        这会产生宽度约 400 像素的列。不过,还有其他一些因素会影响实际宽度。举例来说,如果缩放比例是 50%,那么实际列宽就只有 200 像素。另外,到目前为止,列宽更多地会被看作最小列宽。这意味着,如果可用总宽度为 900 像素,要呈现结果包含两列,并且要充分填满这整个 900 像素的话,就要让每列的宽度都超过定义的 400 像素。通常都需要这样,因为它会让呈现结果看起来非常美观。不过,如果您不想执行该行为,而只希望列宽实际就是 400 像素的话,可以确保列宽不是灵活可变的:

     

     

        现在,所有列都正好是 400 像素(100% 缩放),剩余空间就让它显示为空白。

       
    另一个您可能想尝试的与列相关的设置是列之间的空隙。这可以通过 ColumnGap 属性调整(此设置也是基于与设备无关的像素数):

     

    其中一个相关的设置是列规则,它允许在列之间定义一个可视元素。请看此例(其结果见图 15): 

    15 列之间采用简单规则的流文档 (单击该图像获得较大视图)

     

     

        当然,在许多出版物中,文档并不只是采用简单的列布局。通常还存在从一般流中提取出来的其他元素。您已见过这样的例子,例如将图像置于文档中。图 12 显示了图形设计师常用的一种排列方式。此图像位于两列之间,周围环绕文字,图像方方正正地位于内容中间,并没有影响任何一列的文字布局。这是一种常见的布局选择,只是还不能用于流文档之前我所了解的动态屏幕阅读环境。
       
       
    创建此类布局的关键是图形块,它允许定义不与文档其余部分那样布局的内容。将图像置于图形标记内部就是一例,但图形还有许多其他用途。例如,您可以使用图形来定义横跨整个文档宽度的标题:

     

        在本代码中,图形包含另一个段落,即用作标题的文本。请注意,这里有一些您可用来创建高级、灵活文档的便捷属性。例如,看一下图形的宽度。我没有将宽度设 为特定像素数,而是将其设为内容的确切宽度,这会根据整个内容的宽度自动调整图形宽度。

       
    请看图 16。其中,您会注意到标题(通过图形放置)设为横跨整个内容宽度,这就将所有四列的位置都向下推移了。该图像从垂直和水平方向看都定位于页面中央。 

     

     

     

     

    16 标题横跨四列 (单击该图像获得较大视图)
       
    请注意,其宽度与内容相关的图形不必始终与内容一样宽。以下例来说,图形宽度设为内容宽度的 75%

     

     

     

     

     

        当然,图形高度可通过类似方式定义(虽然图形通常是随着内容纵向变化)。

       
    另一重要方面是图形的位置。在代码段中,它设为横向定位为靠左,纵向定位为靠上。也就是说,图形会出现在当前内容页面的左上角,而无论其实际如何定义。然而在本示例中,图形被定义为文档的第一个元素,但即使该标题之前已有段落,它也会由于这些设置而被上移和左移。图 12 和图 16 中的照片已按类似方式,将其横向定位为“PageCenter”,在列之间移动。(所有这些设置的可用属性值都可以在 WPF 文档中找到)。

       
    您可能已经注意到,本文涉及了大量手动编码。例如,每当需要改变字体时,您都要将该信息添加到块或内嵌元素中。到目前为止,这还不是一个大问题,因为大部分示例都很小。但是,如果有一本每 50 页为一章的书,您要改变每一段的字体,每次都手动来改的话,无疑会很繁重。幸运的是,现在有了一个更好的办法:如 WPF 中的其他任何内容一样,流文档支持样式。样式可被定义为实际流文档中指定名称的资源。以下是定义字体信息的样式:

        然后,该样式会通过以下方式应用到段落(和其他元素):

        由于流文档的特性,样式特别常用。建议您对于最简单情形之外的任何情形,都使用样式来定义大部分格式选项,而不是通过个别内嵌元素的属性。样式可让您的文档保持紧凑,而且更易维护。

       
    总结

       
    希望本文不只让您获得对流文档及其功能的基本了解,而且也激发起您的兴趣。还有许多更高级的功能,包括查看器控件的复杂样式、子类化和延伸文档、块和内嵌 元素、数字权限管理、文本和墨迹注释功能以及高级字体格式等,绝对值得您深入研究。

    宽度也可与其他项相关,例如列宽。下例图形始终是两列宽(除非只显示一列,那样宽度就会减为一列):

     

    11 使用 FlowDocumentReader 控件 (单击该图像获得较大视图)
       
    虽然 FlowDocumentReader 几乎对所有基本使用情形都很有吸引力,但选择怎样的控件还需视您的情况而定。它用途广泛且功能强大,并支持分页布局,这在许多情形下是比滚动更高级的功 能。关于该主题的更详细讨论不在本文探讨范围之内,但事实证明,滚动及重合等相关效果是人们较之数字化文本更喜欢打印文本的主要原因之一。分页方法在许多 情况下更为自然,有助于让数字化阅读被更普遍接受。

       
    那么您如何定义要使用哪个控件呢?一个简单但相当强力的方法是将想要的控件添加到文档的 XAML 中:

    6 Internet Explorer 中显示的 XAML 流文档 (单击该图像获得较大视图)
        Internet Explorer
    (及其他浏览器)可以显示 XAML 内容这一事实特别有趣,因为这是将流文档作为您的 Web 应用程序一部分显示的一张票证。换句话说,如果您将 XAML 流文档上传到您的 Web 服务器,而有人浏览到了该文件,他就会看到类似于图6的效果(假设该用户已安装 Microsoft® .NET Framework 3.0)。当然,这也是动态运作的。如果您的 ASP.NET Web 应用程序(或任何其他服务器端技术)动态生成了一个 XAML 流文档,并将其作为输出返回(假设内容类型已适当设为“application/xaml+xml”),用户就会看到作为您应用程序一部分的流文档,这在 许多情形下必然相当有用。图 7 显示了一个简单的生成流文档的 ASP.NET 页面。

    7 动态 ASP.NET 流文档

    5 文本环绕图片和标题 (单击该图像获得较大视图)

        还有 Span Run 之外的一些内嵌元素。下面就是其他一些更有趣的内嵌元素:

        Figure Figure
    是有些不寻常的内嵌元素,因为它们包含块。因此,从某种意义上讲,Figure 几乎就像流文档内的迷你流文档。Figure 经常用于高级布局功能,例如段落中被普通文本流包围的图像。 
        Floater Floater
    是轻型的图形。它们不支持任何图形放置选项,但是如果您需要的只是除标准段落对齐之外还能做些简单对齐的功能,Floater 会比较有用。
        LineBreak LineBreak
    元素的作用与其名称所指的意义完全相同:它们会在段落内引入换行符。
        InlineUIContainer InlineUIContainer
    BlockUIContainer 的内嵌元素等同项。如果您需要将任何类型的 WPF 控件与您其他的内嵌元素组合使用(例如让一个按钮在一个段落文本内移动),InlineUIContainer 正是您所需要的。
        Figure
    始终用于流文档中(LineBreak 也是如此,不过它们几乎不需要详细讨论)。以下示例使用一个图形,将一个图像显示为一个更大流文档的一部分:

        诸如 FontFamily 等许多属性都可以始终在所有流文档类中找到。例如,若要设置一个完整段落而非只是一个内嵌元素的字体,您不使用 Span 即可做到:

        当然,通过将某一文本段包到粗体或斜体标记中,来直接指定诸如粗体和斜体等属性的功能非常方便和直观,因此通常更多的是使用 <Bold>,而不是 <Span FontWeight="Bold">。不过,<Span> 元素还是非常有用的,因为有许多属性都要设为粗体以外的属性,而且那其中的大多数格式选项都没有单独的 Span 类型。事实上,许多非常常见的格式选项没有特殊的 Span。一个典型的示例就是设置字体。与 HTML 不同,流文档没有 <Font> 元素。相反,字体按如下方式设置:

        Bold Span 的特殊版本,其默认字体粗细设为粗体;Bold 类型由 Span 子类化而来,并且会覆盖 FontWeight 属性。类似特殊的 Span 还有 Italic Underline。不过,这些特殊的 Span 并不是必不可少的,因为您也可以使用默认的 Span,并设置相应属性:

        当然,它要方便得多,您不必使用 XAML 定义每个内嵌元素,但是如果您要以编程方式创建相同示例,了解内嵌元素的概念就非常重要了,因为它们不可以在代码中省略。以下是前面两个 XAML 示例的对等代码段:

        在本例中,该段落包含两个文本段——“Hello”(使用默认格式)和“World!”(粗体)。这比较有趣,因为这表示此 XAML 不只是实例化一个段落,并将其文本设为一个简单的字符串;相反,它创建了含有两个子段的一个段落,每个子段包含不同格式的文本。在 WPF 中,这些段称为内嵌元素。就如一个流文档可以包含多个不同类型的块一样,段落也可以包含各种类型的内嵌元素。内嵌元素有多种变体。有些内嵌元素就是所谓的 Span,它们代表应用了特定格式选项的文本段。此例中的 Bold 元素是 Span 的一个特殊情形,其默认字体粗细设为粗体。内嵌元素的另一种类型是 Run,它是带有默认格式的文本段。因此,上面的 XAML 其实只是下例的简化:

        当然,这远不及 XAML 提供的声明性方法那么直观,因此编程的方法只在特殊情形下采用。(当我需要创建一个格式丰富的报告,结果要更像一份真实的文档,而非通过许多报告引擎创建的表格形式的输出时,有时会使用此方法。)

       
    在许多情形下,段落本身带有格式丰富的内容,这也是通过实例化类实现的,如下所示:

        这会实例化一个 FlowDocument 类和 Paragraph 类(其文本设为“Hello World!”)。该段落被添加到 FlowDocument 的块集合中。请注意,对于所有 XAML 而言,元素名称都区分大小写,并且精确映射到作为 WPF 一部分而提供的类。您也可通过编程方式创建相同文档,如下所示:

    3 带有稍微高级格式的流文档 (单击该图像获得较大视图)
       
    本示例显示的是带有内嵌格式的若干段落。它还提供另一类型块元素的第一个示例,即列表,毫无疑问,它包含多个列表项。请注意,每个列表项反过来也只是包含 更多块元素的容器。因此,我不是简单地将文本置于一个列表项中,而是向每个列表项中添加一个段落元素。就此而论,我应该已向每个列表项或任何其他块类型添 加了多个段落。这可以让您在列表的单个列表项内创建高级布局,这在 HTML 等格式中一般行不通,因为此类格式只会让简单的文本字符串流向每个列表元素。

    流文档基础知识

       
    至此您已了解了一些流文档的基础知识,接下来让我们回顾一下某些基础知识。如您所见,流文档是块的集合。在内部,所有块都是从 System.Windows.Documents.Block 类派生而来的 WPF 类。块又是从 ContentElement 派生而来(沿此链向上追寻几步),ContentElement WPF 中专门为文档定义优化的一个相当低级别的类。此方法有些类似于您用来定义 WPF 界面的控件,它们都从 UIElement 派生而来。两者的继承树在概念上很相似,但并不完全相同。这意味着 WPF 控件和块不能直接组合。例如,一个按钮的标题不能设为一段文本,一个段落也不能直接包含一个按钮。这些控件和块之间存在一些细微差别,这是由于内容控件内 的布局和块内的布局的运作方式截然不同这一事实所致。幸运的是,这两类 WPF 元素之间需要弥合的差异非常小。就按钮而言,它可以包含由带格式的文本构成的 TextBlock 对象;而块可以通过特殊 BlockUIContainer 块类包含任何 WPF 控件。这意味着,流文档可以包含所有类型的 WPF 元素(包括交互式用户界面、媒体和三维元素),而从另一个角度看,流文档也可是任何 WPF 用户界面的一部分,例如可以是控件内容的一个高级布局元素,也可以是一个真正的元素,例如销售点应用程序中的某一项的描述。

       
    可用块的列表理论上是可扩充的,因为开发人员可以派生他们自己的块类,然后创建他们自己的针对文档呈现引擎的增强功能。这提供了我所了解的任何其他文档呈 现引擎都无法提供的自由度。但是,对一般的文档创建者公开的块数量通常有限。图4显示了最重要的块类型的列表。

    4 重要块类型

    说明

    段落

    包含(潜在格式丰富的)文本

    列表

    包含各种不同类型(编号、项目符号等)的列表

    包含与 Microsoft Word HTML 中的表类似的表

    BlockUIContainer

    包含作为整个流的一部分的各种 UI 元素

    包含一组其他块。段对于将常见属性应用于一组块中很方便,例如将同一字体属性应用于多个段落


       

    当使用 XAML 创建 WPF 流文档时,您事实上只要实例化某些类型。请看下面的 XAML 代码段(从此处起,我将省略命名空间定义,以让示例尽量简单):

    1 XamlPad 中显示的极其简单的流文档 (单击该图像获得较大视图)
       
    当然,这是一个非常简单的例子,文档的定义和嵌入式布局会复杂得多。流文档支持您能想到的所有格式,例如斜体、下划线、字体颜色和字体等。图2显示的是一个稍微高级的示例,其结果可在图 3 中看到。

    2 更多格式和项目符号列表

     


     最浮云的一篇,而且图片没贴上还要整理

    展开全文
  • Word 文档转变成网页并显示

    千次阅读 2019-04-26 21:50:38
    我认为在ASP.NET MVC中,要想将Word 文档转变成网页并显示到页面上,需要以下五步: 1、确定选中的是不是Word文档 2、将文件保存到指定的文件夹中 3、将word转换HTML 4、精简筛选 5、保存最终结果 一、确定选中的是...
  • 在MFC中显示一幅图像程序...现在网上很多相关的描述都很不详尽,我总结并编写程序测试后,依次介绍在对话框程序和单文档程序中显示一幅图像。(假设在VS平台上已经按照要求配置好Opencv) 1、 新建一个单文档程序Read
  • 网上下载了一个chm文档,但打开后发现无法显示该页,提示“确保 Web 地址 //ieframe.dll/dnserrordiagoff.htm# 正确。”,通过查找相关资料,迅速了解决该问题,特此分享。 一、CHM文档定义: CHM...
  • JAVA_API1.6文档(中文)

    万次下载 热门讨论 2010-04-12 13:31:34
    javax.swing 提供一组“轻量级”(全部是 Java 语言)组件,尽量这些组件在所有平台上的工作方式都相同。 javax.swing.border 提供围绕 Swing 组件绘制特殊边框的类和接口。 javax.swing.colorchooser 包含供 ...
  • java web 命名规范

    千次阅读 2011-03-06 10:07:00
    4 命名规范  4.1 项目编号命名规范  待完善 4.2 文档命名规范  待完善 4.3 jsp/html命名规范  jsp与html文件名全部小写,并遵循如下的规范: 数据/内容显示页  ...
  • 使用Word的主控文档,是制作长文档最合适的方法。主控文档包含几个独立的子文档,可以用主控文档控制整篇文章或整本书,而把书的各个章节作为主控文档的子文档
  • 索引文档 通过使用 index API ,... 我们可以提供自定义的 _id 值,或者 index API 自动生成。 创建新文档 当我们索引一个文档, 怎么确认我们正在创建一个完全新的文档,而不是覆盖现有的呢? 请记住...
  • C# 命名规范

    千次阅读 2016-02-28 09:34:33
    命名规范是一个十分重要但又比较有争议的话题,本文主要谈一下我的体会并介绍常用的C#命名规范。 匈牙利命名法 我最早接触到的命名规范是匈牙利命名法,该方法出自微软,基本上是一些在你的所有变量前建立一个前缀的...
  • 开发文档

    千次阅读 2018-03-19 20:44:40
    开发文档规范软件开发文档标准
  • docfx 做一个和微软一样的文档平台

    千次阅读 热门讨论 2017-11-29 08:54:36
    开发中,有一句话叫 最不喜欢的是写文档,最不喜欢的是看别人家代码没有文档。那么世界上文档写最 la 好 ji 的就是微软了,那么微软的api文档是如何做的?难道请了很多人去写文档?实际上微软有工具用来生成 api ...
  • layuiadmin开发文档

    万次阅读 多人点赞 2019-04-28 11:13:24
    layuiAdmin pro v1.x 【单页版】开发者文档 更新日志常见问题在线演示会员专区切换到:iframe版文档 layuiAdmin pro (单页版)是完全基于 layui 架构而成的后台管理模板系统,可以更轻松地实现前后端分离,它...
  • 如何写出受技术欢迎的需求文档

    千次阅读 2018-10-31 14:49:01
    正如我们做出来的产品都希望受用户欢迎,开发和测试是需求文档的用户,产品经理也应该重视他们的想法和要求才能写得令人满意。 “写需求文档”说专业点是把用户(或运营、客服等)的需求转化成技术部门的话语,因此...
  • Qt帮助文档使用方法

    千次阅读 2019-10-22 20:22:18
    Qt 帮助文档非常细致而全面,应有尽有,是非常不错的自学教材。因为 Qt 帮助文档太多,所以难以都翻译成中文,即使翻译了一部分,翻译花的时间太多,翻译更新的时效性也难以保证,所以还是得看英文帮助为主。本节...
  • 文件名命名规则

    千次阅读 2011-11-24 10:41:02
    3.文件或文件夹命名的规则 ① 在文件名或文件夹名中,最多可以有255个字符。其中包含驱动器和完整路径信息,因此用户实际使用的字符数小于255。 ② 每一文件一般都有三个字符的文件扩展名(也叫后缀名),用以标识...
  • 使用Doxgen创建Xcode文档

    万次阅读 2011-03-03 17:58:00
    文档集为Xcode开发者提供了一个查找各种文档的便捷方式,包括:API、指南、教程、Q/A、示例代码及其他。 许多开发者都用过苹果文档
  • JavaScript常见变量和函数命名示例

    万次阅读 2020-07-21 15:16:17
    本文的出现相信能够解决大部分烦恼,你轻松写出符合规范、易读、简短的代码。 本文将通过大量的实例来试图自圆其说,形成一套系统化、实用的变量命名理化体系。通过按JavaScript的数据类型分类着手、细到一个函数...
  • MySQL命名、设计及使用规范

    万次阅读 2018-04-03 08:14:46
    库、表、字段全部采用小写,不要使用驼峰式命名。 避免用ORACLE、MySQL的保留字,如desc,关键字如index。 命名禁止超过32个字符,须见名之意,建议使用名词不是动词 数据库,数据表一律使用前缀 临时库、表名必须以...
  • Android开发中java 命名规则包的命名

    千次阅读 2011-09-16 17:57:11
    Android开发中java 命名规则包的命名    Java包的名字都是由小写单词组成。但是由于Java面向对象编程的特性,每一名Java程序员都可以编写属于自己的Java包,为了保障每个Java包命名的唯一性,在最新的Java编程...
  • C#操作Word(二)——将Word文档嵌入到WinForm窗体中

    万次阅读 多人点赞 2010-07-26 18:55:00
    实例说明 ...一切的操作都是这么轻松,实例中将会介绍怎样使用C#在Form窗体中显示Word文档,运行效果如图1所示。 图1 将Word文档嵌入到WinForm窗体中?关键技术 本实例重点在于向读者介绍怎样使用Web...
  • C#类与命名空间

    千次阅读 2021-08-18 23:15:26
    首先是剖析Hello World程序,上篇文章分别用十种技术编写了Hello World程序,其中有简单的也有复杂的,本片文章将剖析其中最简单的,也就是ConsoleApplication,通过剖析这个程序,来大家对类和命名空间有一个初步...
  • 项目 命名规则

    千次阅读 2010-05-05 22:13:00
    命名规则这里规定并说明了设计,编写代码时的命名规则。所有开发人员必须无条件遵守,并按照这个命名规则来命名。如有不足,不合理请提出来,项目开始之前可以更正。※本文没有特别声明的英文字母,和阿拉伯数字全是...
  • 软件编程命名方法+数据库命名

    千次阅读 2014-03-15 10:07:16
    几种常见的程序设计中命名规则   正确并形象地给函数、变量命名,不仅可以增加程序的可读性,也是程序员编程风格的一种反映。较好的命名习惯,可以有效的提高程序的可维护性。以下介绍几种常用的变量命名规则。  ...
  • Android 有一个专用的XML命名空间,用于使工具可以记录XML文件里的信息,并且在打包程序的进行把信息剥离到不会带来运行时期和下载大小的负面影响的程度。 这个命名空间的 URI 是 http://schemas.android.com/tools...
  • 数据表命名和字段命名方法

    千次阅读 2007-08-30 23:53:00
    从实践来看,望文生义,维护效率很高,自己开发时,我很少看文档。 一. 关于大小写因为在编写软件过程中,会遇到很多语句会直接输入sql语句,所以应该作到全部表名,字段名小写。偶尔见到有的人把字段的开头用大写...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 250,575
精华内容 100,230
关键字:

如何让文档命名全部显示