导读
DocumentViewer
其实复杂的事情原本可以很简单. 你想要的那个轮子, 也许别人早就为你造好了. 在这里也是如此. 比如全部你想要的, 就是在你的系统前端可以直接查看DWG文件, 那么全部需要做的, 仅仅是简单的从DWG导出为DWFx文件, 然后使用WPF的DocumentViewer控件打开这个文件就可以了. 这个示例程序可以从这里获取.
- <DocumentViewer Name="docViewer" />
- XpsDocument xpsDocument = null;
- if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
- {
- // If there's an existing document open, close it.
- if (xpsDocument != null)
- xpsDocument.Close();
- // Create an XpsDocument for the file specified by the user.
- try
- {
- xpsDocument = new
- XpsDocument(dlg.FileName, System.IO.FileAccess.Read);
- }
- catch (UnauthorizedAccessException)
- {
- System.Windows.MessageBox.Show(
- String.Format("Unable to access {0}", dlg.FileName ) );
- return;
- }
- // For optimal performance the XPS document should be remain
- // open while its FixedDocumentSequence is active in the
- // DocumentViewer control. When the XPS document is opened
- // with the XpsDocument constructor ("new XpsDocument" above) a
- // reference to it is automatically added to the PackageStore.
- // The PackStore is a static application collection that contains
- // a reference to each open package along the package's URI as
- // a key. Adding a reference of the XPS package to the
- // PackageStore keeps the package open and avoids repeated opens
- // and closes while the document content is being accessed by
- // DocumentViewer control. The XpsDocument.Dispose() method
- // automatically removes the package from the PackageStore after
- // the document is removed from the DocumentViewer control and
- // is no longer in use.
- docViewer.Document = xpsDocument.GetFixedDocumentSequence();
DocumentViewer是WPF专为呈现XPS文件内容准备的控件. 它所呈现的内容叫做FixedDocument, 这完全符合XPS的标准. 我们的DWFx格式文件是与XPS兼容的, 所以也可以被DocumentViewer直接载入和显示.我们用DocumentViewer直接打开一个DWFx文件并仔细对比, 你会发现它所呈现出来的内容和在AutoCAD中精确地相同.
这一特点, 取决于DWG文件导出到DWFx文件的过程中所采取的处理方式. 在转化的过程中, 所有的图形地理信息, 包括直接描述和图块信息引用, 都被原地还原被进行相应的变换(比如旋转, 缩放), 然后将最终的图形信息全部写入到FixedPage文件中. 采取这样的行为, 保证了转换前后的呈现精确问题, 并且这种精确性的实现不依赖于转换后容器的特性.
凡事有一利必有一弊. 这里面有个问题. 我们知道, 如果我们手里有图形的原信息, 有变换公式, 那么我们很容易就能得到变换后的图形. 反之, 如果我们有变换后的图形和变换公式, 虽然可能麻烦一些, 但是大多数情况下我们能得到图形的源信息. 但是, 如果我们有图形的源信息和变换后的图形, 那么在大多数情况下猜测变换公式是极其困难的; 如果我们仅有变换后的图形, 那么还愿图块的图形信息引用, 是根本不可能的. 现在我们得到的DWFx这个包, 就让我们处在了这个比较囧的处境中 - 我们有变换后的图形信息(都在FixedPage中), 除此之外, 关于图形的一切其他细节, 比如它是经过什么变换来的, 它和另外一个图块是不是同一类图形等这些信息, 什么都没有了.
好吧, 这实在是够麻烦的. 不过我们可以有一些折中的办法, 比如, 在设计时, 相同的图块, 我们给它定义一些自定义属性,来标记这些图块原本指代了同一类实体, 比如都指代了电脑. 我们有这些自定义属性就够了. 自定义的属性是可以被获得的, 这些属性都保存在DWFx包中的Object definition file文件中.
自定义属性才是这个系统能够自动化的关键!
比如我们要建设一个网吧自动管理系统. 我们的DWG文件就是包括电脑摆放位置在内的网吧布局设计. 我们的期待能交互的实体是DWG文件中摆放的一台台电脑. 我们期待这个管理系统能自动读入DWG/DWFx文件来表示网吧电脑的陈列位置, 然后我能在这些电脑上点击选择操作, 比如杀毒, 比如下载设置文件等. 要达到这个目的, 我们依赖自定义属性就可以了. 我们可以要求设计厂商在DWG文件中对每一台电脑加上一个电脑编号. 到我们的系统中, 我们可以读出这个自定义属性, 从而实现自动化. DWG/DWFx文件标示了实体的位置, 自定义属性标示了实体的唯一标记并实现系统的互动.
上一篇文章已经详细分析了DWFx包内各个文件的含义. 所以读取这些文件的内容是相对容易的. 不过看起来, 我们首先需要一个标尺类库, 来标示我们应该如何去读取,怎么读.
XPS Packaging类库
.NET Framework在System.Windows.Xps.Packaging命名空间下, 提供了一系列的类型和方法来帮助我们显示和操作XPS文档. 虽然我们的DWFx文件格式上兼容XPS, 但是显然XPS Packaging这个类库是Generic的, 是为所有的XPS文件设计的, 所以不太可能使用这个类库来直接操作我们的DWFx文件内容(从上文的文件内部组织形式也能体现这种差异). 不过作为微软提供的类库, 它应该是我们想要实现的DWFx类库的最好的标杆.
您可以从这个链接来查看和学习XPS Packaging类库: http://msdn.microsoft.com/en-us/library/system.windows.xps.packaging.aspx. 可以注意的几个类是XPSDocument, xxxSequenceReader/Writer, xxxDocumentReader/Writer, xxxPageReader/Writer. 聪明的你一定注意到了, 这几个类的名字几乎正好对应了上文分析的XPS文档的组织形式. 所以如果我们来写DWFx Packaging Library, 也要清楚的显示出DWFx文件内部的层次形式.
DWFx Packaging & Converting Library的实现
废话不多说了, 点击这里下载笔者创建的一个DWFx Packaging & Converting 类库的实现. 这个类库一共包含了4个项目:
JeffreySun.Dwfx : 基础项目, 包含了一些DWFx的基本信息, 以及错误处理等;
JeffreySun.Dwfx.Packing : Packaging类库, 实现读取DWFx文件包内容的功能;
JeffreySun.Dwfx.Converting : Converting类库, 将读取的DWFx文件包内容链接并转化为XAML + 自定义属性
JeffreySun.DWFX2XAMLConverter : 一个简单的示例程序.
您一定要注意这段重要声明! 这一类库仅以学习目的发布, 你可以在你的项目中直接使用, 但是作者不保证该类库的完整性, 也不保证该类库中没有问题, 并且不提供任何技术支持. 作者强烈建议您在研究DWFx格式规范后实现自己的DWFx类库.
至少, 您得改一下类库的名字吧...不然老板会踢你的屁股的 :)