很多书上没有的那种dev控件工具书,怎么加

怎样使用Word 2007控件制作合同书的应用实例
清醒时做事,糊涂时读书,大怒时睡觉,无聊时关注图老师为大家准备的精彩内容。下面为大家推荐怎样使用Word 2007控件制作合同书的应用实例,无聊中的都看过来。
更改的,甚至不允许更改。另外一些内容,比如涉及到公司的名称、地址、帐号等不允许出错,还包括一些可选的项目等。这种类似网页表单的文本用Word怎样实现呢?请您跟我来,按照下面的步骤一步步熟悉,马上就可以做出一份满足上述要求的书,它定会给您以后的工作带来极大方便。图:效果1.建立文档在编写采购合同时候,按照正常的步骤输入文档,需要注意的是留出需要单独填写的部分。在下面的步骤中,我们将最常用的文本输入格式文本,日期,下拉列表,复选框等内容来介绍给大家。还有一点要注意,那就是要为Word添加选项卡,这是我们实现后期添加窗体控件的必要条件。在初始状态下,在菜单栏中是没有开发工具这个选项的。这时候,可以单击图标(Office系列软件左上角),然后选择Word
选项,在常用标签中选择在功能区显示开发工具选项卡,就可以看到了。选开发工具2.添加控件我们先来仔细看一看开发工具中的控件窗口,针对不同的项目,我们可以添加不同的控件。控件显然,最常见的是文本内容的输入,它对应于控件窗口中前两个,分别叫做格式文本和纯文本。回到刚开始那份合同界面,首先遇到的是合同编号,那么它显然是一个文本输入.由于在合同封面,所以还要有一定的格式要求,这就要插入一个格式文本控件,这也是它与纯文本控件的区别。我们插入格式文本后后作如下设置:新建样式可以看看输入完毕时候的样子。输入完毕后的效果同样的方法,完成下方甲乙双方的文本控件的添加。在签订日期后需要插入一个日期选择器,它的属性我们在这里就不详述了,主要是格式问题,这很好的解决了不统一的问题。与上面两项内容操作有些不同的是对于下拉菜单的设置。在后面的开户行列表中,需要选择不同的名称,添加一个下拉列表的空间来实现此功能,在最后生成的文档中,可以直接选择下拉菜单中的内容,而不用进行文本输入,以避免不必要的错误。保护文档制作完成后,是这样的情况。制作后的情况3.文档保护和分发至所有的区域设置完成后,文档的基本操作完成,但是这还不是我们想要的。送到客户手中的文本应该是部分锁定的,只能在窗体中添加内容。如何对文档进行保护,避免非正常的内容变更,这就需要对文档进行保护。这就要在开发工具使用保护文档功能。中有一个,这时专门用来对文档进行加密,防止恶意篡改,为文本添加。如图:为文档添加密码至此,我们就完成了所有合同书内容的制作。本文主要介绍控件的使用方法,它不仅可以实现文本内容和日期的输入格式的规范化,还可以通过下拉菜单来避免不必要的失误(特别是对于一些号码和认别,还有利于实现一些称呼不用的概念的统一输入)。而且通过保护文档可以有效实现文件的安全。(注:完成最后的定稿后,可以将其保存为一个模板,这样的模板除了相应的窗体部分可以修改之外,其余都是只读状态,在诸如的调查问卷和情况登记中,有极大的应用价值。)& 搜索“图老师”或者“tulaoshi_com”加关注,每天最新的美食、、、美妆、、手工DIY等教程让你一手全掌握。推荐关注!【扫描下图可直接关注】
来源:/n/1392.html
点击查看更多与《》相关的文章>>
故障恢复控制台应用实例,故障恢复控制台应用实例
生活已是百般艰难,为何不努力一点。下面图老师就给大家分享word域的概念、作用及使用范例,希望可以让热爱学习的朋友们体会到设计的小小的乐趣。
实例操作:PEAR的HTML_QuickForm7应用,实例操作:PEAR的HTML_QuickForm7应用
用Word 2007自创书法字帖,用Word 2007自创书法字帖
使用SetMask脚本制作沿路径运动的遮罩实例,使用SetMask脚本制作沿路径运动的遮罩实例
asp使用activex组件实例一,asp使用activex组件实例一
热门搜索:
照片PS 打造夜景荷花效果教程
CAD基础教程 怎么找回删除没有保存的文件
快捷键没有反应怎么处理
打开文件显示安全警告怎么办
自动榴弹炮坦克是现代军事武器,与车辆构成一体,越野性能好杀伤范围大。通过学习自动榴弹炮简笔画来了解更多军事知识。今天我们将通过自动榴弹炮儿童简笔画步骤图解来学习自动榴弹炮儿童简笔画的画法。
自动炮是一种现代化军事武器,由炮管炮口炮托等部件构成。小朋友可以学习自动炮简笔画的同时了解更多军事相关知识。接下来我们就通过自动炮简笔画教程图解步骤来学习怎么画自动炮简笔画。
自动手枪是可以自动填发装弹的手枪,和左轮枪不同的是自动手枪更趋近于现代常用枪支。通过学习自动手枪简笔画来了解枪支结构。接下来我们就通过自动手枪儿童简笔画步骤图解来学习怎么画自动手枪简笔画吧!
左轮枪是经典的手枪武器之一,又名转轮枪,其特色即是可以弹巢子弹替换时在左侧为名。今天我们将了解左轮枪的详细构造。通过学习左轮枪简笔画教学步骤图解来了解如何画左轮枪简笔画吧!
怎么利用Photoshop给电商模特照片精修磨皮?很多朋友在工作中经常会遇到要求精修模特的情况,特别是做化妆品行业的电商设计师,所以总结了一下我的模特修图方法和技巧与大家分享,喜欢的朋友让我们一起来学习吧。
磨皮是我们ps照片最基本的技能,Photoshop人物磨皮是我们必须掌握的,如今不少国人对Photoshop修图的三大酷炫神技双曲线、中性灰与高低频几近痴迷,抓耳挠腮想学会它,下面一起看看教程吧。
掌握一点Photoshop教程对你是非常有用的,这篇教程是给大家分享PS简单快速创建眼睛彩虹美瞳效果方法,教程最终创建出来的效果非常漂亮,而且难度并不是很大,很值得大家学习,一起来学习吧。
2017年最受欢迎的生活服务APP!!生活服务类App的出现为日常生活带来极大便利,我们可以通过手机客户端预约各种家政服务,足不出户就能够享受实惠、便捷的生活服务,那么接下来我为大家推荐一些生活中必备生活app。
由于现在抢红包大热起来,很多app都开始支持发红包抢红包了,因此也出现了微信抢红包神器,可以自动抢微信红包,肯定比认为手动操作快,有消息就会自动打开,然后打开红包。今天就给大家分享2017微信自动抢红包神器大全。
2017手机赚钱软件排行榜!!很多朋友都在苦恼每天的钱不够话,空闲时间不知道怎么打发,总想寻找一种简单的赚钱方式,我们的手机app就有很多,下面具体给大家介绍手机赚钱软件有哪些,总有一款你中意的。
经常玩微信的朋友都知道,微信小程序已经全面上线了,对于小程序的出现是否会引发App变革众说纷纭,但不可否认的是小程序能够带给用户及产商一定的便利,那么今天为大家汇总目前上线的一些实用小程序。
男女交往一直都是人们比较关注的一个问题,都说相爱容易,相处不易,交往过的朋友应该都能深刻的体会到,女生常常抱怨着让男生理解自己,但男生也希望多体谅一下他们。尤其不要老是问一些无法回答的问题来考验他们。
很多朋友都信风水一说,对于自己家里房屋客厅的装修也是有一定要求的,因为客厅的风水关系着整个家居的运势,更关系着主人的财运与健康,因而客厅里的风水禁忌是需要我们谨慎注意的,那么风水学中客厅风水禁忌有哪些呢?
春天已经来临了,夏天也不远了,新的一才刚刚开始,时尚爱美的美眉们最关注的就是春夏服装设计的流行趋势了,早早的把握住时尚趋势走在流行的最尖端,今天就给大家整理分享2017春夏女装设计流行趋势 ,你绝对不能错过的亮点。欢迎加入我们,一同切磋技术。 &
用户名: &&&
密 码: &
共有 1210 人关注过本帖
标题:CommonDialog 控件哪里设置纸张大小?
等 级:侠之大者
帖 子:136
专家分:409
结帖率:100%
&&已结贴√
&&问题点数:20&&回复次数:8&&&
CommonDialog 控件哪里设置纸张大小?
CommonDialog控件哪里设置纸张大小?即从哪里可以将纸张大小传递到了printer.PaperSize ?好像CommonDialog控件不接收纸张大小设定?
等 级:侠之大者
帖 子:136
专家分:409
各位版主大人,也不帮帮我吗?
来 自:湖北
等 级:版主
威 望:95
帖 子:1004
专家分:5228
最简单的方法是在打印服务里添加一个自定义的纸张大小,然后遍历打印机打印尺寸,遍历打印尺寸的代码如下:
&&Dim i As Integer, j As Integer
&&On Error Resume Next&&&'如果是喷墨或激光打印机不能选择自定义纸张,所以要做出错处理
&&Printer.ScaleMode = 7&&'打印尺寸以厘米为单位
&&For i = 0 To 256
&&& Printer.PaperSize = i
&&& If Err.Number = 0 Then If Printer.ScaleWidth = 我定义的纸宽 And Printer.ScaleHeight = 我定义的纸高 Then j = i
&&If j & 0 Then MsgBox &没有找到你自定义的纸张大小或打印机不支持自定义纸张大小&
一般来说,针式打印机都支持自定义的纸张,喷墨或激光打印机虽然可以在打印服务自定义,但不能选择该尺寸打印,如果选择会出错。vb也可以用代码设置自定义的纸张,但比较复杂,使用好几个api
等 级:侠之大者
帖 子:136
专家分:409
问题是如果打印机默认纸张是A4,但这次我选用了A3,如何将选择结果传递到printer对象呢?CommonDialog控件没有接收纸张大小的属性啊?Printer有paperSize属性,可人机互动不是靠CommonDialog控件来传递消息么?版主以上的代码虽遍历了打印机能打印的所有尺寸,但它却怎么知道我这次选用的尺寸?我的打印机是A3、A4纸同时装在打印机上的(A4是装在底盒里,A3是装在机侧,打印时可自由选择,但默认是A4)。
来 自:湖北
等 级:版主
威 望:95
帖 子:1004
专家分:5228
Printer.PaperSize = 8是a3纸
等 级:侠之大者
帖 子:136
专家分:409
CommonDialog控件打开的打印对话框中有纸张选项,但好像CommonDialog控件本身却没有PaperSize属性,那通过什么将我要设的纸张大小传给Printer.PaperSize呢?
总不能再在窗体上增加一个ComboBox控件来专门接收纸张大小啊,这样也太搞笑了点。
请各位高手帮帮我!
等 级:侠之大者
帖 子:136
专家分:409
回复 5 楼 xzlxzlxzl
我知道 Printer.PaperSize = 8 是A3纸,但这个8我通过窗口什么传给程序呢,如果就这样写进代码里,那就永远只能用A3纸打印了
来 自:湖北
等 级:版主
威 望:95
帖 子:1004
专家分:5228
&&得分:20&
CommonDialog.ShowPrinter的设置结果并不和printer对象关联,由于CommonDialog.ShowPrinter并不输出其纸张类型,只能通过变通了(如果不使用对话框就很容易解决了),在窗体里添加一个CommonDialog1控件和一个command1控件,输入如下代码:
Private Declare Function GetDeviceCaps Lib &gdi32& (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Sub Command1_Click()
&&Dim h As Long, w As Long, h1 As Long, w1 As Long, i As Integer, j As Integer
&&On Error Resume Next
&&CommonDialog1.CancelError = True
&&CommonDialog1.Flags = cdlPDPrintSetup + cdlPDReturnDC
&&CommonDialog1.ShowPrinter
&&If Err.Number = 32755 Then Exit Sub&&&&&&&&&'选择了取消
&&w = GetDeviceCaps(CommonDialog1.hDC, 110)&&&'获取CommonDialog1打印纸宽
&&h = GetDeviceCaps(CommonDialog1.hDC, 111)&&&'获取CommonDialog1打印纸高
&&Printer.ScaleMode = 3: j = -1
&&For i = 0 To 255
&&& Err.Clear
&&& Printer.PaperSize = i
&&& If Err.Number = 0 Then
&&&&&&w1 = GetDeviceCaps(Printer.hDC, 110)&&&'获取Printer打印纸宽
&&&&&&h1 = GetDeviceCaps(Printer.hDC, 111)&&&'获取Printer打印纸高
&&&&&&If w1 = w And h1 = h Then
&&&&&&&&j = i
&&&&&&&&Exit For
&&&&&&End If
&&& End If
&&If j & 0 Then
&&& MsgBox &打印纸尺寸类型为& & j & &,代码为: Printer.PaperSize=& & j
&&& MsgBox &出错&
&&'Me.PrintForm&&&&&'打印窗体
&&'Printer.EndDoc&&&'结束打印
等 级:侠之大者
帖 子:136
专家分:409
回复 8 楼 xzlxzlxzl
今天停一天电,晚上刚来电。谢谢版主解答!
版权所有,并保留所有权利。
Powered by , Processed in 0.130088 second(s), 7 queries.
Copyright&, BCCN.NET, All Rights Reserved近十年来,我国制造业持续快速发展,总体规模大幅提升,综合实力不断增强,不仅对国内经济和社会发展做出了重要贡献,而且成为支撑世界经济的重要力量。你绝对想不到,中国制造有这么多“世界第一”
在此可输入您对该资料的评论~
(window.slotbydup = window.slotbydup || []).push({
id: '4540180',
container: s,
size: '250,200',
display: 'inlay-fix'
热门资料排行
添加成功至
资料评价:
所需积分:0C#入门学习-----图书阅读器(WPF 用户控件技术)
欢迎大家提出意见,一起讨论!
转载请标明是引用于
需要源码请与我联系。
&编译平台:VS2008 + .Net Framework 3.5
&&&&&&& 语言: C#
1、图书阅读器系统架构
1、2 系统架构设计
在这个系统中出现在的实体有图书目录、图书列表、图书、压缩格式的图书、图像缓存等。
(1) 文件夹可以直接定义为一个类。因为该对象相对固定,不同的文件夹除了名称唾位置不一样外,还可能会有一些其他变化的特性。
(2)每个文件夹包含多部书。因为图书的类型不是固定的,比如有压缩文件类型的图书和其他格式的图书,需要抽象出来实现一个接口。
(3) 每本书包含多个页面。因为每个页面的格式是不同的,因些也需要进行抽象。
(4) 每本图书会包含一个图像缓存,该缓存提供的功能相对固定,当然也可以进一步抽象。
Catalog代表一个文件夹类,它包含代表该目录下所有图书的ObservableCollection&IBook&泛型集合类。
IBook是抽象出来的代表一部图书的接口,它实现了INotifyPropertyChanged以便实现UI级别的绑定。
BaseBook是一个实现了IBook接口的类,提供了对于每本图书的基本实现。
RarBook通过派生自BaseBook类,实现了压缩格式的图书对象;
IBookItem接口是代表图书书页的接口,IBook接口包含一个类型为List&IBookItem&泛型集合,来表示一本书的所有图书页。
RarPage实现了IBookItem接口,提供了对于RarBook类型图书的书页实现。
1、3 项目文件夹介绍
Dependencies文件夹包含了项目中使用到的第三方类库或程序,比如pdftohtml.exe用于将pdf文件转换为Html格式。
&&&&&&&&&&&&&&&&&&&&&&&&& SevenZipSharp.dll用于压缩或解压缩文件,使用的时候需要7z.dll来进行压缩或解压缩。
&&&&&&&&&&&&&&&&&&&&&&&& WPFToolkit.dll包含一些额外的控件来丰富WPF控件。
项目根目录下的app.config是应用程序配置文件。
2、系统核心类的实现
这一节将介绍如何实现。主要内容涵盖了.NET的反射、多线程、操作文件和文件夹知识,以及如何使用面向对象方式设计和实现类。
2、1& 实现图书目录Catalog类
图书阅读器每次在启动时,会根据在选项指定的文件路径异步加载图书到ListBox以显示书籍。
或者用户单击“打开”按钮,从弹出的打开文件窗口中选择一个文件。
Catalog会将该文件加载到其图书列表中,Catalog类要能从文件夹中枚举图书文件,也要能从特定文件中加载图书。
从图可以看出,Catalog包含实现了IBook接口的实例列表,作为其内含的图书。
因为该类不被设计用于继承或开,因此将该类指定为Internal.
internal class Catalog
Catalog类定义了3个属性,分别用于指定文件路径、用于保存图书的列表及一个布尔值获取和设定图书变更信息。
图书列表采用泛型集合,原因是因为它采用了集合通知。
#region -----------------属性区域-----------------
private string _bookPath = string.E//文件路径
public string BookPath
//文件路径属性
get { return _bookP }//返回值
set { _bookPath = }//设置值
private ObservableCollection&IBook& _Books =//图书集合
new ObservableCollection&IBook&();
public ObservableCollection&IBook& Books
//图书集合属性
get { return _B }
//返回图书列表
set { _Books = } //设置图书列表
private bool _IsChanged =//是否变更
public bool IsChanged
//是否变更属性
get { return _IsC }
//返回变更
set { _IsChanged = } //设置变更
#endregion
&当在选项中设置好图书的路径后,每次启动程序时,会从app.config中读取设置好的图书路径,再调用重载的确Load()方式从路径中加载图书。
Load有两个重载方法。
一个接受一个文件路径作为参数,该路径将会被赋给Catalog对象的BookPath属性;
另一个Load()方法会根据该属性的值来从目录中加载图书。Load带参数的重载方法实现如下:
public void Load(string path)//该重载方法传入一个文件路径
_bookPath =//将路径指定给BookPath属性
//调用不带参数的重载的Load方法
catch (Exception err) //在加载过程出现错误则触发异常
//调用定制的异常管理窗口显示异常信息
ExceptionManagement.Manage(&Catalog:LoadPath&, err);
代码内部调用了Load另一个重载,如果产生异常,则会产生定制的ExceptioMamagerment类的Mange()方法来产生一个异常窗口。
Load() 方法实现了加载的所有核心逻辑。
private void Load()
//不带参数的重载方法实现
string bin = System.Reflection.Assembly.//得到所保存的书签文件路径
GetExecutingAssembly().Location.Replace(&.exe&, &.bin&);
if (File.Exists(bin)) //如果存在书签
if (LoadBooks(bin))//加载书签
bin = System.Reflection.Assembly. //得到保存的封面文件路径
GetExecutingAssembly().Location.Replace(&.exe&, &.bin2&);
if (File.Exists(bin))
//如果存在封面
//使用一个后台线程异步的加载图书封面
Thread t = new Thread(new ParameterizedThreadStart(LoadCovers));
t.IsBackground =//指定线程为后台线程
t.Priority = ThreadPriority.BelowN//指定线程优先级较低
t.Start(bin); //开始执行线程,并传入Bin参数
//如果加载书签失败
ParseDirectoryThread();//通过分析文件夹重建书签
else //如果书签文件不存在
ParseDirectoryThread();//通过分析文件夹重建书签
catch (Exception err) //产生异常
//显示一个异常信息窗口,列明异常信息
ExceptionManagement.Manage(&Catalog:Load&, err);
代码首先使用System.Reflection.Assembly.GetExecutingAssembly返回当前执行的程序集,获取其Location属性的值,
即程序集的位置。
调用Replace()方法将exe扩展名替换为bin扩展名,得到一个与可执行文件相同的.bin文件,这个文件
保存了图书的文件和文件夹信息,该信息作为书签以二进制格式保存,如果该文件存在,则调用LoadBooks()方法加载书签;
另一个与可执行文件具有相同文件名,扩展名为bin2的文件,保存的是每本图书的封面,如果存在,代码使用一个参数线程后台加载图书
封面信息。
2、2 加载书签信息
图书阅读器尽量保存用户所读过的书的历史信息,以便下次打开软件时,能直接从前一次的位置开始阅读。
因此在每次关闭软件时,会调用Save()方法来保存这些信息。LoadBooks()方法将从保存的二进制文件中恢复历史记录。
private bool LoadBooks(string fileName)//从文件中加载图书集合信息
bool result = //默认结果值
IFormatter formatter = new BinaryFormatter();//实例化二进制格式化器
Stream stream = new FileStream(fileName,
//创建一个FileStream打开文件流
FileMode.Open,
FileAccess.Read,
FileShare.None);
//从流中反序列化出文件目录
string booksFrom = (string)formatter.Deserialize(stream);
//如果图书路径与当前目录位于不同的路径
if (this._bookPath != booksFrom || !Directory.Exists(this._bookPath))
//新建一个books集合类
this._Books = new ObservableCollection&IBook&();
result =//加载失败
else //如果是同一个文件夹
//首先反序列化出图书数目
int count = (int)formatter.Deserialize(stream);
for ( int i = 0; i & i++ ) //循环图书数目
//反序列化出每个文件的文件路径
string filePath = (string)formatter.Deserialize(stream);
long size = (long)formatter.Deserialize(stream);//文件大小
int nbPages = (int)formatter.Deserialize(stream);//页数
string bookmark = (string)formatter.Deserialize(stream);//书签
bool isread= (bool)formatter.Deserialize(stream);//是否阅读
FileInfo file = new FileInfo( filePath );//获取文件信息
if( file.Exists )//如果文件存在
IBook bk =//初始化实现IBook接口的对象
//如果书签过滤设置中包含与文件一致的扩展名
if (Properties.Settings.Default.BookFilter.
Contains(file.Extension.ToUpper()))
bk = (IBook)new RarBook(file.FullName, false);//返回一个新的Rar书本对象
bk.Bookmark =//指定书签
//指定大小
bk.NbPages = nbP
//指定页数
bk.IsRead =
//指定是否阅读
this._Books.Add(bk);
//加载到书签列表
catch( Exception err ) //如果出现加载异常
//在异常处理窗口中显示异常信息
ExceptionManagement.Manage(&Catalog:LoadBooks&, err);
stream.Close(); //文件流使用完成后要关闭以释放非托管资源
//返回结果
LoadBooks要根据保存的顺序从二进制文件中反序列化保存的数据,因此代码首先实例化了一个二进制序列化对象
BinaryFormatter,然后使用FileStream打开文件,使用二进制序列化对象一步一步地进行反序列化。
如果文件的路径与当前反序列化的文件路径不一样,那么系统会初始化一个新的Books集合,并返回加载失败。
如果位于同一文件夹,将继续反序列化流中保存的图书,首先得到图书的数量,然后循环依次反序列化图书文件的详细信息。
如果图书文件存在,则实例化一个新的RarBook 对象,并使用反序列化的信息初始化这个对象,然后加载到图列表中。
2、3 加载图书封面
加载图书封面的LoadCovers() 方法,该方法将在一个后台线程中实现封面的加载,封面将被异步地加载到用户界面的ListBox中。
因为封面资料被保存到另一个二进制文件中,也需要使用反序列化从流中加载信息。
public void LoadCovers(object fileName)//加载封面
IFormatter formatter = new BinaryFormatter();//实例化二进制格式化器
Stream streamBin =
//加载封面文件
new FileStream((string)fileName,
FileMode.Open,
FileAccess.Read,
FileShare.None);
//反序列化图书数目
int count = (int)formatter.Deserialize(streamBin);
for (int i = 0; i & i++)
//遍历图书数目
//反序列化文件路径
string filePath =
(string)formatter.Deserialize(streamBin);
//反序列化内存流,这个过程即便不存在内存流也需要进行反序列化
MemoryStream coverStream =
(MemoryStream)formatter.Deserialize(streamBin);
foreach (IBook book in this._Books)
//遍历图书列表
if (book.FilePath == filePath) //如果文件路径相同
MemoryStream stream2 = new MemoryStream();//新建一个内存流
coverStream.WriteTo(stream2);//将封面流写入内存流中
coverStream.Flush();
//刷新封面流
coverStream.Close();
//关闭封面流
stream2.Position = 0;
//重定位内存流
//调用Invoke方法,在与UI相同的线程中异步的更新图片
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Normal, (ThreadStart)delegate
BitmapImage myImage = new BitmapImage();
myImage.BeginInit();
//开始更新
myImage.StreamSource = stream2;//指定流来源
myImage.DecodePixelWidth = 70;//指定图片宽度
myImage.EndInit();
//结束更新
book.Cover = myI
//将图书封面指定为该BitmapImage
coverStream =
//释放封面流
//释放内存流
catch (Exception err)//如果产生异常
//在与UI相同的线程中调用异常显示窗口
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Normal, (ThreadStart)delegate
//使用自定义的ExceptionManagement类
ExceptionManagement.Manage(&Catalog:LoadCovers&, err);
streamBin.Close();//关闭文件流以释放资源
LoadCovers()在后台线程中执行,而该线程与UI不处于同一线程,要调用UI线程中的方法,必须使用Dispatcher.Invoke()方法,
传入要执行的方法。
&2、4 多线程图书搜索
现在回到Load()方法中,如果在加载书签失败或不存在书签文件,那么Load()方法会调用ParseDirectoryThread()方法在一个后台
线路中递归文件夹,得到书签信息。
internal void ParseDirectoryThread()//使用后台线程获取图书书签信息
Books.Clear();//清除图书列表
Thread t = new Thread
//在后台线程中调用ParseDirectoryRecursive方法
(new ParameterizedThreadStart(ParseDirectoryRecursive));
t.IsBackground =
//指定为后台线程
t.Priority = ThreadPriority.BelowN//指定线程优先级别
t.Start(_bookPath); //为线程方法传入文件夹路径
catch (Exception err)
//如果产生异常
//调用自定义的异常信息窗口
ExceptionManagement.Manage(&Catalog:ParseDirectoryThread&, err);
ParseDirectoryThread方法首先清除图书列表,然后实例化一个参数化的线程,在后台线程中调用ParseDirectoryRecursive递归解析
传入的文件夹路径。该方法实现了重获书签信息的核心逻辑。
internal void ParseDirectoryRecursive(object path)//递归获取图书书签信息
//实例化DirectoryInfo对象
DirectoryInfo directory = new DirectoryInfo((string)path);
if (!directory.Exists)//如果目录不存在
//在UI线程中显示提示信息
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Normal, (ThreadStart)delegate
MessageBox.Show(&目录不存在! 请检查选项对话框&);
//退出方法
} //如果目录存在,则调用GetFiles方法获取目录下所有的文件
foreach (FileInfo file in directory.GetFiles(&*.*&))
//判断图书文件扩展名列表中是否包含指定文件的扩展名
if (Properties.Settings.Default.
BookFilter.Contains(file.Extension.ToUpper()))
//如果包含,则在UI线程中实例化RarBook对象
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Background, (ThreadStart)delegate
//实例化一个新的RarBook对象
IBook bk = (IBook)new RarBook(file.FullName, true);
bk.Size = file.L //指定文件大小
Books.Add(bk);
//添加到列表中
this.IsChanged =//设置Ischanged状态为true
foreach (DirectoryInfo dir in
//循环遍历目录下的子目录
directory.GetDirectories(&*&, SearchOption.TopDirectoryOnly))
//通过递归调用自身搜索子目录中的文件
ParseDirectoryRecursive(dir.FullName);
catch (Exception err)
//如果产生了异常
//在UI线程中调用ExceptionManagement的Manage方法
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Normal, (ThreadStart)delegate
//在UI线程中显示异常信息
ExceptionManagement.Manage(&Catalog:ParseDirectoryRecursive&, err);
//方法返回
//方法返回
ParseDirectoryRecursive是一个不断调用自身的过程。
因为Books这个集合是一个泛型的ObservableCollection&IBook&类,该类将要与UI进行绑定来自动更新UI,
而IBook实现了INotifyPropertyChanged接口。同样地,在一本书的属性信息变化时触发UI的变更,对于Books集合的
增删改必须要与UI处于同一线程,因此使用了Dispatcher 的Invoke()方法。
2、5 保存图书信息
public void Save()//保存封面和书签信息
if (IsChanged) //如果图书列表发生变化
//移除没有封面的图书
RemoveDirtyBooks();
//保存书名和书签
string bin = System.Reflection.Assembly.//获取书签文件名
GetExecutingAssembly().Location.Replace(&.exe&, &.bin&);
SaveBooks(bin);//调用SaveBooks方法保存书签
bin = System.Reflection.Assembly.
//获取封面文件名
GetExecutingAssembly().Location.Replace(&.exe&, &.bin2&);
SaveCovers(bin);//调用SaveCovers方法保存封面信息
catch (Exception err)//如果触发异常
//显示异常提示窗口
ExceptionManagement.Manage(&Catalog:Save&, err);
2、6 刷新图书列表
在UI主线程中,当用户单击“刷新”按钮时会调用Catalog 类的Refresh() 方法,因为图书阅读器是基于文件和文件夹这种存储模式,文件和文件夹可能会发生变化。
那么通过刷新机制可以从事Books集合中移除不存在的文件或文件夹。Refresh使用一个后台线程调用RefreshThread()方法。
// 从目录中加载图书列表
public void Refresh()
// 带参数的线程委托
Thread t = new Thread(new ParameterizedThreadStart(RefreshThread));
t.IsBackground =
t.Priority = ThreadPriority.BelowN
t.Start(_bookPath);
catch (Exception err)
ExceptionManagement.Manage(&Catalog:Refresh&, err);
internal void RefreshThread(object o)//刷新图书列表
//首先,刷新己经不存在的图书
// List也为泛型类型
List&IBook& temp = new List&IBook&();
foreach (IBook book in this._Books)
//循环遍历判断图书文件是否存在
if (!File.Exists(book.FilePath))
temp.Add( book );//不存在则加入到移除图书列表
foreach (IBook book in temp)//遍历要移除的图书列表
Application.Current.Dispatcher.Invoke
因为refresh是在后台,而我们的移除的内容是在用户界面,
// 所以要用主线程中的方法
(DispatcherPriority.Normal, (ThreadStart)delegate
//在UI线程中移除图书
// 因为_Books是与ListBox绑定的变量,这样就能在界面上删除图书
_Books.Remove(book);
//重新从文件中加入图书列表
ParseDirectoryRecursiveWithCheck(_bookPath);
catch (Exception err)//如果产生异常
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Normal, (ThreadStart)delegate
//在UI线程中显示异常处理窗口
ExceptionManagement.Manage(&Catalog:RefreshThread&, err);
代码首先实例化一个新的List&IBook&泛型列表,循环Books集合,判断指定的图书对应的文件是否存在,如果不存在则加入到List&IBook&中准备移除,
然后在UI线程中进行循环移除;
然后调用ParseDirectoryRecursiveWithCheck()方法,该方法与ParseDirectoryRecursive类似,是一个递归方法,该方法主要不同的是调用了
BookExist() 方法来判断图书的文件路径与当前的文件路径是否一致。
2、7& 定义图书接口IBook
BookReader当前支持的图书类型有限,仅RarBook这一类,但是系统在最初架构时,已经提供了弹性方式允许将来扩充
多种图书文件格式,其BaseBook实现了IBook接口,开发人员可以通过派生自BaseBook类来实现多种格式图书类。
BaseBook实现了IBook接口,IBook接口又被Catelog引用,使用这种基于接口的方法可以实现程序间的解耦,
使程序具有良好的可扩充性。
internal interface IBook : INotifyPropertyChanged
string FileName { }//文件名称
int NbPages { }//图书页数
long Size { }
//文件大小
bool IsRead { } //是否阅读
string Bookmark { }//书签
BitmapImage Cover { } //封面
IBookItem CurrentPage { }//书页
string FilePath { }
//文件路径//书页集合
//图像缓存
BitmapImage GetCurrentPageImage();//当前书页
void GotoMark();
//定位书签
bool GotoNextPage();//转到下一页
bool GotoPage(IBookItem page);//定位到指定页
bool GotoPreviousPage();//转到上一页
void Load(); //加载图书
void ManageCache();//管理缓存
void SetMark(); //设置书签
void UnLoad();
//卸载图书
IBook接口定义了一本书基本属性和方法,该接口派生自INotifyPropertyChanged接口,当图书信息发生变化时,
可以向UI触发属性变更通知。
2、8 图书基类BaseBook
BaseBook也要实现INotifyPropertyChanged接口的成员员,BaseBook类与其他类的关系如下:
RarBook从BaseBook基类派生,提供了对于Rar格式图书的实现。BaseBook包括ImageCache图像缓存。
BaseBook的Pages包含实现了IBookItem接口的对象集合,CurrentPage用于显示当前的图书页面。
该类重载了构造函数,提供了一个接收文件路径的构造函数,当文件路径发生变化时,会触发INotifyPropertyChanged接口
中定义的变更通知。
public BaseBook(string filePath)
_filePath = fileP //得到文件路径
RaisePropetyChanged(&FilePath&);//触发文件路径变更通知
RaisePropetyChanged(&FileName&);//触发文件变更通知
下面从3个方面介绍BaseBook实现的功能:
(1) 实现书签功能: BaseBook允许用户定义或跳转到书签,提供书签列表功能。
public void SetMark()
//将当前页面的路径赋给Bookmark
Bookmark = _CurrentPage.FileP
public void GotoMark()
//如果_Bookmark路径不为空
if (!string.IsNullOrEmpty(_Bookmark))
foreach (IBookItem pg in Pages)
//如果页面路径与书签路径相同
if( pg.FilePath == _Bookmark )
_CurrentPage = //指定当前页面
SetMark()方法主要是记录当前页面的文件路径,该值被赋给Bookmark属性,而BookMark属性会触发属性变更通知,以便UI能够知晓变化。
(2)&&实现页面导航:BaseBook提供上一页、下一页或定义到指定页。
public bool GotoPage( IBookItem page )//定位到指定页面
//循环遍历页面
foreach (IBookItem pg in Pages)
//如果页面与指定页面一致
if (pg == page)
//设置当前页面
_CurrentPage =
//返回设置成功
//否则设置失败
public bool GotoNextPage()//跳转到下一页面
//得到当前页面的索引值
int next = Pages.IndexOf(_CurrentPage);
if (next &= Pages.Count-1)//判断是否越界
//如果越界则返回
next = next + 1; //让索引值加1转到下一页
_CurrentPage = Pages[next];//设置当前页为下一页
//返回设置成功标志
public bool GotoPreviousPage()//跳转到上一页面
//得到上一页面的索引值
int next = Pages.IndexOf(_CurrentPage);
if (next == 0) //如果值为0则不能再上一页
//返回导航失败标记
next = next - 1;//减少一页
_CurrentPage = Pages[next];//设置当前页为上一页
//返回设置成功标记
(3) 实现基本的图像缓存: BaseBook提供了基本的缓存功能。
2、9 图书页面接口IBookItem的定义
internal interface IBookItem //图书页面接口
string FilePath { }//文件路径
string FileName { }//文件名称
&3、设计BookReader用户主界面
3、1 设计系统主界面
WPF用户界面的设计与传统的Winodw Forms的UI设计有了明显的区别,在WPF中,UI设计通常使用
而局软件进行UI布局。
BookReader的主界面使用一个Grid控件将整个面板分为4行。因了BookReader要显示圆滑的边框,所以需要将主窗口的背景设置为透明色,
并且去掉Windows自带的标题栏。在声明属性时,将其Backgroud属性设置为Transparent,设置WindowsStyle为None。
添加一个Grid控件,使用该Grid将覆盖整个客户端区域使用RowDefinitions集合编辑器将这个grid划分为4行。
因为将WindowStyle属性设置为None后,需要为主界面自己添加最小化、最大化和关闭按钮。
窗体阅读区域位于第3行,在该行内部又嵌入一个Grid,这个Grid将中间分为3列,分别用于旋转ListBox,Splitter和一个用来显示书面的图像的用户控件。
3、2 实现主窗口样式的绑定
在主界面中,大多数控件的Style使用了DynamicResource这个动态资源关键字绑定到了样式。
在WPF中,资源分为以下两类:
(1)静态资源:使用StaticResource进行指定,静态资源在第一次编译后即确定其对象或值,之后不可修改。
(2)动态资源:使用DynamicResource指定,在运行时决定,当运行时才会到资源目录中查找其值。
例如,在主界面XAML文件中,Border用来为主界面实现圆角边框,使得窗体看起来很圆滑,一些按钮具有特别样式,都使用
DynamicResource进行设定。
那么这些资源是定义在哪里呢?打开App.xaml文件,就可以看到在应用程序集,使用ResourceDictionary合并了几个资源文件。
3、3 实现图书列表界面
图书列表信息被绑定到一个ListBox控件上,图书阅读面板上使用了一个控件来显示图片信息,中间使用一个自定义的GridSplitterExpander控件来实现分割条。
ListBox控件的DataContext将绑定到Catalog对象的Books集合上,用来显示图书封面和图书详细信息。
当用户双击图书时,在图书阅读界面显示图书,该ListBox控件被放在一个Grid的左侧列中。
每当加载图书目录时,ListBox会被绑定到Catalog的Books集合上,加载图书目录的代码写在LoadCatalog() 方法内部,
该方法在主窗体加载时会被调用。
private void LoadCatalog()
//加载图书目录
_Catalog.Load(Properties.Settings.Default.Catalog);
//指定ListBox控件的数据绑定
CatalogListBox.DataContext = _Catalog.B
this.Splitter.Title = //指定中间分割条的文本
string.Format(&CATALOG ({0} book(s))&, _Catalog.Books.Count);
CatalogListBox的DataContext被指定到_Catalog.Books集合,然后ListBox的ItemSource指定到了集合中的元素,
具体的呈现交给了命名样式CatalogCoverStyle。(CatalogCoverStyle通过下图指定)
该样式位于Resources文件夹下的Shared.xmal的定义中,指定了ListBox的数据模板和而已面板。
(Shared.xmal是在App.xml中被合并到资源中了)
Shared.xmal中定义了三部分内容:
(1)ListBox的整体数据模板
(2)单个元素指定的样式
(3)BaseBook的数据模板
3、4 实现图书阅读界面
图书阅读面板中间是一个可折叠的自定义控件,该控件也显示了当前打开图书的页数,可以单击上面的方向箭头进行折叠和展开。
分割条与PageView控件的声明XAML如下:
4、实现用户界面功能
本节将介绍如何调用核心层中的功能来实现阅读器的运行。
4、1 实现工具按钮事件
最大化和最小化只是改变主窗体的WindowState来控制;
对于退出按钮,可以直接调用主窗体的Close()方法来关闭窗体,实现如下:
//标题栏的关闭按钮事件处理代码
private void closeButton_Click(object sender, RoutedEventArgs e)
this.Close();//Close方法关闭主窗体
//标题栏的最大化按钮事件处理代码
private void maximizeButton_Click(object sender, RoutedEventArgs e)
//首先判断当前WindowState的状态是否是最大化
if (this.WindowState == WindowState.Maximized)
//如果为最大化,则设置为标准样式
this.WindowState = WindowState.N
else //否则设置为最大化样式
this.WindowState = WindowState.M
//将窗口最小化事件处理代码
private void minimizeButton_Click(object sender, RoutedEventArgs e)
this.WindowState = WindowState.M//指定最小化
4、1、1、页面适应按钮
调整宽度和高度的按钮,其事件处理代码通过调用用户控件PageViewer的方法来实现,代码如下:
private void btnFitWidth_Click(object sender, RoutedEventArgs e)
//通过设置PageViewer控件的FitWidth属性来设置宽度
this.SimplePageView.FitWidth();
private void btnFitHeight_Click(object sender, RoutedEventArgs e)
//通过设置PageViewer控件的FitHeight来设置宽度
this.SimplePageView.FitHeight();
4、1、2 打开图书按钮
///打开一个不在当前文件夹的外部文件
private void btnOpen_Click(object sender, RoutedEventArgs e)
using (System.Windows.Forms.OpenFileDialog
//实例化一个OpenFileDialog对象
browser = new System.Windows.Forms.OpenFileDialog())
if (browser.ShowDialog() == //显示打开文件对话框
System.Windows.Forms.DialogResult.OK)
//调用Catalog的Open方法打开文件,将返回的IBook实例加载到图书列表
LoadBook( (IBook)_Catalog.Open(browser.FileName) );
在代码中,使用using语句块实例化一个OpenFileDialog对象,在超过该using语句块的作用域时,该对象将自动
释放掉。根据获取到的所要打开的文件名, 调用Catalog对象的Open() 方法打开该文件。
然后调用LoadBook将打开的文件加载到PageViewer控件及图书列表中。
LoadBook是定义在主窗体中的一个辅助方法,该方法专用于加载指定IBook对象实例的图书到UI对象PageViewer控件中。
//加载一部指定的图书
private void LoadBook( IBook book )
if (_CurrentBook != null)//首先卸载CurrentBook图书
_CurrentBook.UnLoad();
_CurrentBook =//将当前图书指定为传入的图书
_CurrentBook.Load();//加载图书到图书对象中
this.SimplePageView.Scale = 1.0;//指定缩放比率
//指定当前图书页面图像
this.SimplePageView.Source = _CurrentBook.GetCurrentPageImage();
//指定当前Label控件显示图书路径
this.PageInfo.Content = _CurrentBook.CurrentPage.FileP
//滚动到图书的开始位置
this.SimplePageView.ScrollToHome();
catch (Exception err)
//如果产生异常,显示异常信息窗口
ExceptionManagement.Manage(&Main:LoadBook&, err);
在代码中,产生指定_CurrentBook对象,当前一本书加载进来时,将设置_CurrentBook对象,然后将PageViewer控件的Source
指定从GetCurrentPageImage()方法返回的当前页面。
4、1、3 选项按钮
private void btnOptions_Click(object sender, RoutedEventArgs e)
{ //显示选项对话框
//实例化选项对话框
OptionWindow dlg = new OptionWindow();
if (dlg.ShowDialog() == true)//显示对话框
//如果变更了属性设置
if (dlg.NeedToReload)
//重新加载整个图书
LoadCatalog();
catch (Exception err)//出现异常
//显示异常窗口
ExceptionManagement.Manage(&Main:btnOptions_Click&, err);
4、1、4 全屏按钮
private void btnFullScreen_Click(object sender, RoutedEventArgs e)
if (_isFullSreen)//当前是否全屏的布尔字段
//恢复全屏状态
this.WindowState = WindowState.N
_isFullSreen =
//重置全屏布尔字段
Splitter.IsExpanded =
else //如果当前不是全屏状态
//将窗口最大化
this.WindowState = WindowState.M
_isFullSreen =//设置全屏状态
Splitter.IsExpanded =//将分割条进行折叠
catch (Exception err)//如果出现错误
//显示异常并记录错误信息
ExceptionManagement.Manage(&Main:btnFullScreen_Click&, err);
将WindowState设置为Maximized来使窗口最大化而实现全屏,同时使自定义的控件Splitter控件进行折叠以模拟全屏效果。
4、2 实现上下文菜单事件处理
添加标签、定位到标签和移除标签菜单项的实现代码如下:
//将当前图书的当前页面设置为书签
private void MenuItem_BookMark(object sender, RoutedEventArgs e)
if (_CurrentBook != null)//仅在当前图书不空才能设置书签
_CurrentBook.SetMark();//将当前图书页面的路径指定为当前书签
_Catalog.IsChanged =//设置书签变更标志
catch (Exception err)
//如果产生异常显示异常信息
ExceptionManagement.Manage(&Main:MenuItem_BookMark&, err);
//如果当前图书有设置书签,则定位到当前书签
private void MenuItem_GotoBookMark(object sender, RoutedEventArgs e)
//判断当前图书是否是在图书列表中选择图书
if (_CurrentBook != (IBook)CatalogListBox.SelectedValue)
//如果不是,则重新加载选中的图书
LoadBook((IBook)CatalogListBox.SelectedValue);
_CurrentBook.GotoMark();//定位到书签页面
//获取当前页面的图书
this.SimplePageView.Source = _CurrentBook.GetCurrentPageImage();
this.SimplePageView.ScrollToHome();//滚动到页开始处
catch (Exception err)
//如果有异常显示异常信息
ExceptionManagement.Manage(&Main:MenuItem_GotoBookMark&, err);
//清除书签
private void MenuItem_ClearBookMark(object sender, RoutedEventArgs e)
//清除当前选中图书的书签
((IBook)CatalogListBox.SelectedValue).Bookmark = string.E
_Catalog.IsChanged =//设置IsChanged标志以便保存书签
catch (Exception err)
//如果产生异常,显示异常处理信息
ExceptionManagement.Manage(&Main:MenuItem_ClearBookMark&, err);
书签只能对当前图书进行设置,因此需要具有_CurrentBook值,调用&SetMark()方法用于将当前阅读的路径记录到内部的BookMark属性中。
IsChanged属性设置为true后,在保存书签到文件时,会保存到文件中去。
UI端如何在图书列表上添加一个书签图标和阅读外观呢?
可以在XMAL中将一个Border和BitmapImage控件绑定到了IsRead和Bookmark属性上,可以参考Shared.xaml资源文件中的
CatalogCoverStyle&样式定义。
4、3& 创建PageViewer用户控件
该控件在内部使用一个Image控件来显示图书,为了使图书阅读器与目前市面上流行的阅读器软件具有类似的功能,
该Image控件需要处理和种鼠标和键盘事件来实现图书阅读器效果。
4、4 PageViewer 控件属性定义
PageViewer定义了3个属性,这3个属性用来改变PageViewer的内部行为。
//指定自动缩放类型
public AutoFit AutoFitMode
get { return
//获取自动缩放属性值
(AutoFit)Properties.Settings.Default.UseAutoF }
//指定缩放的大小属性
public double Scale
get { return _ }
UpdateScale(); //更新屏幕缩放
//要显示的图像源
public ImageSource Source
get { return this.PageImage.S }
set { this.PageImage.Source = }
在代码中,AutoFitMode属性根据用户在选项面板中的设置来指定自动缩放的类型;
Scale属性用来指定缩放大小,主要根据用户在右下角的Slider控件的返回值来设定Image控件的缩放大小;设置后会调用UpdateScale()方法,
该方法将使用ScaleTransform对象为Image控件设置缩放变换;
/// &summary&
/// 更新图像控件的缩放,并触发事件
/// &/summary&
private void UpdateScale()
this.scaleTransform.ScaleX = _//指定x缩放值
this.scaleTransform.ScaleY = _//指定y缩放值
//指定变换中心点
this.scaleTransform.CenterX = 0.5;
this.scaleTransform.CenterY = 0.5;
//触发变换事件
RaiseZoomChanged();
UpdateScale通过设置缩放变换的ScaleX、ScaleY指定缩放大小,最后调用RaiseZoomChanged解发缩放路由事件。
4、5 定义PageViewer控件路由事件
路由事件是一种可以针对元素树中的多个侦听器(而不是针对引发该事件的对象)调用处理程序的事件。
在WPF中,对用户界面进行布局与传统的Windows Forms有些不一样,在WPF中,用户界面是由一个对象树组成,称为逻辑树。在Vs2010中,用户可以在大纲视图
中看到整棵逻辑树。在WPF中,还有一棵树,称为可视树。可视树将所有的节点打散到核心的可视组件中,而不是将每个元素当作一个黑盒。
例如一个ListBox,在逻辑树上是一个单独的元素,但是在视觉上是由多个元素组成的。因为WPF中的这种特性,路由事件设计的目的是专门用于在元素树中使用的事件,
当路由事件解发后,事件可以向上或向下遍历视觉树和逻辑树,使用一种简单而持久的方式在每个元素上解发。
WPF中的路由事件是一个可传递的事件,事件可以沿着视觉树向上和向下传递,因此事件可以被多个视觉元素捕获来决定是否处理。
RaiseZoomChanged事件将在MainWindow.xmal中被订阅,以便在图像缩放后,能触发缩放滑动块自动切换位置行为。
在主窗体的XMAL声明中,关联了ZoomChanged事件,代码如下:
ZoomChanged&是一个路由事件,因此事件在解发后会以冒泡的形式通知其视觉树中的上层元素,使得视觉树的其他元素有机会处理该事件。
SimplePageView_ZoomChanged的代码如下:
/// &summary&
/// 当页面缩放后更新滑动条控件的位置
/// &/summary&
private void SimplePageView_ZoomChanged
(object sender, PageViewer.ZoomRoutedEventArgs e)
this.zoomSlider.ValueChanged -= //清除滑块控件的事件处理器
new RoutedPropertyChangedEventHandler&double&(this.Slider_ValueChanged);
this.zoomSlider.Value = Math.Round( e.Scale * 100, 0);//更新滑块的值
this.zoomSlider.ValueChanged += //重新关联滑块的事件处理器
new RoutedPropertyChangedEventHandler&double&(this.Slider_ValueChanged);
在代码中,因为滑块的值变化后,会触发ValueChanged事件,而该事件又会设置PageViewer的Scale属性,这样会形成循环触发事件,
所以代码先去掉了对于ValueChanged事件的关联,而设置完值后再重新关联事件。
/// &summary&
/// 触发放大缩小路由事件
/// &/summary&
protected void RaiseZoomChanged()
//定义路由事件参数实例
ZoomRoutedEventArgs args = new ZoomRoutedEventArgs(_scale);
args.RoutedEvent = ZoomChangedE//指定路由事件代码
RaiseEvent(args);//引发路由事件
&路由事件的定义:
路由事件和 . NET事件的定义有一些区别。路由事件的定义是由公共的静态RoutedEvent成员加一个约定的Event后缀组成,
路由事件需要在.NET事件系统中进行注册。
然后路由事件也有一个和普通.NET事件一样的事件定义,或者是一个事件包装器,使得可以像使用普通事件那样使用路由事件。
/// &summary&
/// 注册路由事件
/// &/summary&
public static readonly RoutedEvent
ZoomChangedEvent = EventManager.RegisterRoutedEvent(&ZoomChangedEvent&,
RoutingStrategy.Bubble,
typeof(ZoomChangedEventHandler), typeof(PageViewer));
/// &summary&
/// 事件处理委托
/// &/summary&
public delegate void ZoomChangedEventHandler(object sender, ZoomRoutedEventArgs e);
/// &summary&
/// 路由事件的普通属性定义
/// &/summary&
public event ZoomChangedEventHandler ZoomChanged
add { AddHandler(ZoomChangedEvent, value); }
remove { RemoveHandler(ZoomChangedEvent, value); }
在代码中,定义了一个ZoomChangedEventHandler类型的委托,首先调用定义一个名为ZoomChangedEvent的RoutedEvent,通过调用
EventManager.RegisterRoutedEvent()方法向WPF的事件系统注册路由事件。
RoutingStrategy枚举用于指定路由策略,路由策略是指事件在触发后,事件如何在元素树中传递的方式,有如下3种可选:
Bubble:冒泡传递,事件首先在源元素上触发,然后从每一个元素上沿着树传递,直到根元素为止;
Tunneling: 逐道传递,事件首先在根元素上被触发,依次向源元素传递。
&============================================================================================================================
&4、6 处理屏幕滚动
当按下PageDown或向下方向键时,会调用ManageScroolDown()方向进行滚动,相反会调用ManageScroolUp()处理向上滚动。
这两个方法实现了屏幕滚动和翻页的操作。
//处理键盘事件
private void PageContent_PreviewKeyUp(object sender, KeyEventArgs e)
if (e.Key == Key.LeftShift)//如果用户控下左边的Shift键
//显示放大镜工具
Magnifier.Display(Visibility.Hidden);
//释放页面事件铺获
this.PageContent.ReleaseMouseCapture();
//己处理该预览事件,下面的元素不再处理
e.Handled =
//如果按下PageDown或向下方向键
if (e.Key == Key.PageDown || e.Key == Key.Down)
ManageScroolDown();//处理向下滚动
e.Handled =
//如果按下PageUp或向上方向键
else if (e.Key == Key.PageUp || e.Key == Key.Up)
ManageScroolUp();//处理向上滚动,并触发事件
e.Handled =
//处理ScrollViewer向上滚动
private void ManageScroolDown()
//如果滚动条向上偏移量加上可视高度大于垂直大小
if (this.PageContent.VerticalOffset +
this.PageContent.ViewportHeight &=
this.PageContent.ExtentHeight)
//如果不用到页面底部
if (!WaitAtBottom)
//设置该属性的值
WaitAtBottom =
else WaitAtBottom =
//触发页面变更事件
RaisePageChanged(1);
catch (Exception err)
//如果出现异常显示异常信息
ExceptionManagement.Manage(&PageViewer:ManageScroolDown&, err);
实际上ManageScroolDown并没有处理滚动,而是设置了滚动的状态后,将滚动工作交给了PageChanged事件。
MainWindow.maml.cs的PageChanged事件处理中,将根据传入的PageRoutedEvnetArgs参数来进行实际的滚动操作。
代码如下:
//处理滚动和页面变更
private void SimplePageView_PageChanged
(object sender, PageViewer.PageRoutedEventArgs e)
if (e.PageOffset == -1) //如果是向上跳转页面
if (_CurrentBook.GotoPreviousPage())//跳转到上一页面
//当前页面将为上一页面
this.SimplePageView.Source = _CurrentBook.GetCurrentPageImage();
//指定主页面的文件路径信息
this.PageInfo.Content = _CurrentBook.CurrentPage.FileP
//滚动到上一页面的顶部
this.SimplePageView.ScrollToBottom();
if (e.PageOffset == 1) //如果是要向下跳转页面
if (_CurrentBook.GotoNextPage())//跳到下一页
//显示下一页面
this.SimplePageView.Source = _CurrentBook.GetCurrentPageImage();
//指定下一页面的文件路径
this.PageInfo.Content = _CurrentBook.CurrentPage.FileP
//滚动到页面顶部
this.SimplePageView.ScrollToHome();
在代码中,根据PageRoutedEventArgs传入是否翻页值,调用_CurrentBook的GotoPreviousPage或GotoNextPage来进行上下翻页,
并调用ScrollToBootom或ScrollToHome滚动到页面的底部或顶部。
ScrollViewer控件本身能处理上下方向键进行上下滚动的工作,但是不理解向上或向下翻页的行为。通过处理
PageContent_PreviewKeyUp事件,在到达屏幕顶部或底部时可以进行上下翻页,大大提升阅读体验。
&4、7& 控制鼠标滚轮
两种行为:
(1) 在按下Ctrl+鼠标滚轮,会对图书页面进行放大或缩小
(2) 如果不按下Ctrl,将进行屏幕滚动的工作。
ScrollViewer本身可以处理鼠标滚轮的动作,但是不理解上下翻页的行为,
因此可以为其添加翻页功能。
//处理鼠标滚轮事件
private void PageContent_PreviewMouseWheel
(object sender, MouseWheelEventArgs e)
//如果按下了键盘左边的Ctrl键
if (Keyboard.IsKeyDown(Key.LeftCtrl))
//更新屏幕内容,进行大小缩放
UpdateContent(e.Delta & 0);
e.Handled =
if (e.Delta & 0)//如果是向上滚动
ManageScroolUp();//向上翻页
ManageScroolDown();//向下翻页
4、8 实现页面拖动效果
4、9 创建放大器用户控件
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?}

我要回帖

更多关于 c 控件说明书 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信