WPF中使用流文档灵活地显示内容
?WPF中使用流文檔靈活地顯示內(nèi)容
????????????????????????????by:?Markus Egger
??????????????????????????? form:?http://msdn.microsoft.com/msdnmag/issues/07/08/wpf/default.aspx?loc=zh
Windows??Presentation Foundation (WPF) 提供了一系列功能。事實(shí)上,功能是如此之多,以至于其中一些非常重要的功能都沒有得到應(yīng)有的關(guān)注。一個(gè)最好的例子就是“流文檔”功能,它可讓開發(fā)人員在 WPF 中本機(jī)創(chuàng)建文檔。在《MSDN??雜志》2006 年 1 月期的“XPS 文檔:創(chuàng)建 XML 文件規(guī)范文檔所用的 API 初探”中,Bob Watson 讓我們詳細(xì)了解 WPF 中的 XPS 文檔,但“流文檔”則不同。XPS(XML 文件規(guī)范)針對打印和面向頁面的內(nèi)容,而“流文檔”則針對屏幕顯示以及提供更動(dòng)態(tài)和可以論證的更復(fù)雜模型。“流文檔”幾乎適用于與文本內(nèi)容相關(guān)的所有方面,從產(chǎn)品說明到整本書籍。
?
文本顯示無疑是更重要的 UI 功能之一。在 WPF 界面中,您通常使用標(biāo)簽等控件來顯示文本。但是在許多情形下,您需要的不只是簡單地顯示幾個(gè)單詞。流文檔提供了一種更高級的方法,而它們實(shí)質(zhì)上非常簡單。它們通過類似 HTML 文檔的格式定義文本流,但其功能更強(qiáng)大,并可提供明顯更先進(jìn)的布局選項(xiàng)。
通常使用基于 XML 的標(biāo)準(zhǔn)標(biāo)記語言——可擴(kuò)展應(yīng)用程序標(biāo)記語言 (XAML) 來定義“流文檔”。XAML 對于流文檔特別直觀,主要是因?yàn)樗c HTML 類似。以下流文檔示例創(chuàng)建了一段文字,并只對其中幾個(gè)單詞應(yīng)用了粗體格式:
?
<FlowDocumentxmlns=’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 中更明顯。真正的元素名稱是不同的,但是至少對簡單的文檔來說,模式非常相似。流文檔一般以包含多個(gè)塊的 FlowDocument 根元素開頭。“塊”是指流內(nèi)的元素,通常是如上例所示的文本段落(當(dāng)然還有其他塊類型)。段落又可以包含其他元素,例如本例中的兩個(gè)粗體單詞。請注意,對于任何其他 XAML 文檔,根元素必須包含 XAML 特定的命名空間定義,否則無法被識別。這是 XAML 特定的實(shí)現(xiàn)細(xì)節(jié),與流文檔無關(guān)。請注意,命名空間定義只在獨(dú)立的流文檔中才需要。(流文檔可以是更大的 XAML UI 的一部分,在這種情況下,該 UI 的根元素中會包含命名空間定義。)
當(dāng)然,用戶永遠(yuǎn)不會看到流文檔的 XAML(而 HTML 源則可在瀏覽器中查看),這與他們無法看到任何其他 UI 元素的 XAML 一樣。相反,用戶看到的是文檔的最終呈現(xiàn)。對于這個(gè)特定的示例,您可通過多種方式看到結(jié)果。或許最簡單的方式是將其鍵入 Windows SDK 附帶的實(shí)用工具 XamlPad 中(請參見圖?1)。
圖 1?XamlPad 中顯示的極其簡單的流文檔 (單擊該圖像獲得較大視圖)
當(dāng)然,這是一個(gè)非常簡單的例子,文檔的定義和嵌入式布局會復(fù)雜得多。流文檔支持您能想到的所有格式,例如斜體、下劃線、字體顏色和字體等。圖?2?顯示的是一個(gè)稍微高級的示例,其結(jié)果可在圖?3?中看到。
圖 3?帶有稍微高級格式的流文檔 (單擊該圖像獲得較大視圖)
本示例顯示的是帶有內(nèi)嵌格式的若干段落。它還提供另一類型塊元素的第一個(gè)示例,即列表,毫無疑問,它包含多個(gè)列表項(xiàng)。請注意,每個(gè)列表項(xiàng)反過來也只是包含更多塊元素的容器。因此,我不是簡單地將文本置于一個(gè)列表項(xiàng)中,而是向每個(gè)列表項(xiàng)中添加一個(gè)段落元素。就此而論,我應(yīng)該已向每個(gè)列表項(xiàng)或任何其他塊類型添加了多個(gè)段落。這可以讓您在列表的單個(gè)列表項(xiàng)內(nèi)創(chuàng)建高級布局,這在 HTML 等格式中一般行不通,因?yàn)榇祟惛袷街粫尯唵蔚奈谋咀址飨蛎總€(gè)列表元素。
流文檔基礎(chǔ)知識
至此您已了解了一些流文檔的基礎(chǔ)知識,接下來讓我們回顧一下某些基礎(chǔ)知識。如您所見,流文檔是塊的集合。在內(nèi)部,所有塊都是從 System.Windows.Documents.Block 類派生而來的 WPF 類。塊又是從 ContentElement 派生而來(沿此鏈向上追尋幾步),ContentElement 是 WPF 中專門為文檔定義優(yōu)化的一個(gè)相當(dāng)?shù)图墑e的類。此方法有些類似于您用來定義 WPF 界面的控件,它們都從 UIElement 派生而來。兩者的繼承樹在概念上很相似,但并不完全相同。這意味著 WPF 控件和塊不能直接組合。例如,一個(gè)按鈕的標(biāo)題不能設(shè)為一段文本,一個(gè)段落也不能直接包含一個(gè)按鈕。這些控件和塊之間存在一些細(xì)微差別,這是由于內(nèi)容控件內(nèi)的布局和塊內(nèi)的布局的運(yùn)作方式截然不同這一事實(shí)所致。幸運(yùn)的是,這兩類 WPF 元素之間需要彌合的差異非常小。就按鈕而言,它可以包含由帶格式的文本構(gòu)成的 TextBlock 對象;而塊可以通過特殊 BlockUIContainer 塊類包含任何 WPF 控件。這意味著,流文檔可以包含所有類型的 WPF 元素(包括交互式用戶界面、媒體和三維元素),而從另一個(gè)角度看,流文檔也可是任何 WPF 用戶界面的一部分,例如可以是控件內(nèi)容的一個(gè)高級布局元素,也可以是一個(gè)真正的元素,例如銷售點(diǎn)應(yīng)用程序中的某一項(xiàng)的描述。
可用塊的列表理論上是可擴(kuò)充的,因?yàn)殚_發(fā)人員可以派生他們自己的塊類,然后創(chuàng)建他們自己的針對文檔呈現(xiàn)引擎的增強(qiáng)功能。這提供了我所了解的任何其他文檔呈現(xiàn)引擎都無法提供的自由度。但是,對一般的文檔創(chuàng)建者公開的塊數(shù)量通常有限。圖?4?顯示了最重要的塊類型的列表。
當(dāng)使用 XAML 創(chuàng)建 WPF 流文檔時(shí),您事實(shí)上只要實(shí)例化某些類型。請看下面的 XAML 代碼段(從此處起,我將省略命名空間定義,以讓示例盡量簡單):
?
<FlowDocument><Paragraph>Hello World!</Paragraph> </FlowDocument>?
這會實(shí)例化一個(gè) FlowDocument 類和 Paragraph 類(其文本設(shè)為“Hello World!”)。該段落被添加到 FlowDocument 的塊集合中。請注意,對于所有 XAML 而言,元素名稱都區(qū)分大小寫,并且精確映射到作為 WPF 一部分而提供的類。您也可通過編程方式創(chuàng)建相同文檔,如下所示:
?
FlowDocument doc = new FlowDocument(); Paragraph para = new Paragraph(); para.Inlines.Add(“Hello World!”); doc.Blocks.Add(para);?
當(dāng)然,這遠(yuǎn)不及 XAML 提供的聲明性方法那么直觀,因此編程的方法只在特殊情形下采用。(當(dāng)我需要?jiǎng)?chuàng)建一個(gè)格式豐富的報(bào)告,結(jié)果要更像一份真實(shí)的文檔,而非通過許多報(bào)告引擎創(chuàng)建的表格形式的輸出時(shí),有時(shí)會使用此方法。)
在許多情形下,段落本身帶有格式豐富的內(nèi)容,這也是通過實(shí)例化類實(shí)現(xiàn)的,如下所示:
?
<Paragraph>Hello <Bold>World!</Bold></Paragraph>?
在本例中,該段落包含兩個(gè)文本段——“Hello”(使用默認(rèn)格式)和“World!”(粗體)。這比較有趣,因?yàn)檫@表示此 XAML 不只是實(shí)例化一個(gè)段落,并將其文本設(shè)為一個(gè)簡單的字符串;相反,它創(chuàng)建了含有兩個(gè)子段的一個(gè)段落,每個(gè)子段包含不同格式的文本。在 WPF 中,這些段稱為內(nèi)嵌元素。就如一個(gè)流文檔可以包含多個(gè)不同類型的塊一樣,段落也可以包含各種類型的內(nèi)嵌元素。內(nèi)嵌元素有多種變體。有些內(nèi)嵌元素就是所謂的 Span,它們代表應(yīng)用了特定格式選項(xiàng)的文本段。此例中的 Bold 元素是 Span 的一個(gè)特殊情形,其默認(rèn)字體粗細(xì)設(shè)為粗體。內(nèi)嵌元素的另一種類型是 Run,它是帶有默認(rèn)格式的文本段。因此,上面的 XAML 其實(shí)只是下例的簡化:
?
<Paragraph><Run>Hello </Run><Bold>World!</Bold> </Paragraph>?
當(dāng)然,它要方便得多,您不必使用 XAML 定義每個(gè)內(nèi)嵌元素,但是如果您要以編程方式創(chuàng)建相同示例,了解內(nèi)嵌元素的概念就非常重要了,因?yàn)樗鼈儾豢梢栽诖a中省略。以下是前面兩個(gè) 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 的特殊版本,其默認(rèn)字體粗細(xì)設(shè)為粗體;Bold 類型由 Span 子類化而來,并且會覆蓋 FontWeight 屬性。類似特殊的 Span 還有 Italic 和 Underline。不過,這些特殊的 Span 并不是必不可少的,因?yàn)槟部梢允褂媚J(rèn)的 Span,并設(shè)置相應(yīng)屬性:
?
<Paragraph>Hello <Span FontWeight=”Bold”>World!</Span></Paragraph>?
當(dāng)然,通過將某一文本段包到粗體或斜體標(biāo)記中,來直接指定諸如粗體和斜體等屬性的功能非常方便和直觀,因此通常更多的是使用 <Bold>,而不是 <Span FontWeight="Bold">。不過,<Span> 元素還是非常有用的,因?yàn)橛性S多屬性都要設(shè)為粗體以外的屬性,而且那其中的大多數(shù)格式選項(xiàng)都沒有單獨(dú)的 Span 類型。事實(shí)上,許多非常常見的格式選項(xiàng)沒有特殊的 Span。一個(gè)典型的示例就是設(shè)置字體。與 HTML 不同,流文檔沒有 <Font> 元素。相反,字體按如下方式設(shè)置:
?
<Paragraph>Hello <Span FontFamily=”Comic Sans MS” FontSize=”24”>World!</Span></Paragraph>?
諸如 FontFamily 等許多屬性都可以始終在所有流文檔類中找到。例如,若要設(shè)置一個(gè)完整段落而非只是一個(gè)內(nèi)嵌元素的字體,您不使用 Span 即可做到:
?
<Paragraph FontFamily=”Comic Sans MS” FontSize=”24”>Hello World!</Paragraph>?
還有 Span 和 Run 之外的一些內(nèi)嵌元素。下面就是其他一些更有趣的內(nèi)嵌元素:
Figure? Figure 是有些不尋常的內(nèi)嵌元素,因?yàn)樗鼈儼瑝K。因此,從某種意義上講,Figure 幾乎就像流文檔內(nèi)的迷你流文檔。Figure 經(jīng)常用于高級布局功能,例如段落中被普通文本流包圍的圖像。
Floater? Floater 是輕型的圖形。它們不支持任何圖形放置選項(xiàng),但是如果您需要的只是除標(biāo)準(zhǔn)段落對齊之外還能做些簡單對齊的功能,Floater 會比較有用。
LineBreak? LineBreak 元素的作用與其名稱所指的意義完全相同:它們會在段落內(nèi)引入換行符。
InlineUIContainer? InlineUIContainer 是 BlockUIContainer 的內(nèi)嵌元素等同項(xiàng)。如果您需要將任何類型的 WPF 控件與您其他的內(nèi)嵌元素組合使用(例如讓一個(gè)按鈕在一個(gè)段落文本內(nèi)移動(dòng)),InlineUIContainer 正是您所需要的。
Figure 始終用于流文檔中(LineBreak 也是如此,不過它們幾乎不需要詳細(xì)討論)。以下示例使用一個(gè)圖形,將一個(gè)圖像顯示為一個(gè)更大流文檔的一部分:
?
<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 塊。相反,圖像以標(biāo)準(zhǔn)的 WPF Image 控件內(nèi)嵌為 BlockUIContainer。(相同的方法也用于流文檔內(nèi)諸如視頻或交互式三維模型等內(nèi)容)。圖?5顯示了與此類似的一個(gè)文檔的呈現(xiàn)。
圖 5?文本環(huán)繞圖片和標(biāo)題 (單擊該圖像獲得較大視圖)
查看流文檔
現(xiàn)在,您已了解如何創(chuàng)建一些簡單的流文檔以及如何在 XamlPad 中查看它們。而目前我所忽略的是該如何在自然狀態(tài)下查看流文檔。畢竟,您不會期望用戶打開 XamlPad,然后粘貼文檔的 XAML。查看 XAML 流文檔的一種方法是將其另存為一個(gè)擴(kuò)展名為 .xaml 的文件,然后在 Windows 資源管理器中雙擊它。這會啟動(dòng)與 XAML 文件相關(guān)聯(lián)的默認(rèn)應(yīng)用程序(通常是 Internet Explorer?),從而顯示該文檔。結(jié)果如圖?6?所示。
圖 6?在 Internet Explorer 中顯示的 XAML 流文檔 (單擊該圖像獲得較大視圖)
Internet Explorer(及其他瀏覽器)可以顯示 XAML 內(nèi)容這一事實(shí)特別有趣,因?yàn)檫@是將流文檔作為您的 Web 應(yīng)用程序一部分顯示的一張票證。換句話說,如果您將 XAML 流文檔上傳到您的 Web 服務(wù)器,而有人瀏覽到了該文件,他就會看到類似于圖?6?的效果(假設(shè)該用戶已安裝 Microsoft??.NET Framework 3.0)。當(dāng)然,這也是動(dòng)態(tài)運(yùn)作的。如果您的 ASP.NET Web 應(yīng)用程序(或任何其他服務(wù)器端技術(shù))動(dòng)態(tài)生成了一個(gè) XAML 流文檔,并將其作為輸出返回(假設(shè)內(nèi)容類型已適當(dāng)設(shè)為“application/xaml+xml”),用戶就會看到作為您應(yīng)用程序一部分的流文檔,這在許多情形下必然相當(dāng)有用。圖?7?顯示了一個(gè)簡單的生成流文檔的 ASP.NET 頁面。
顯示流文檔
您可能已經(jīng)注意到,每當(dāng)顯示流文檔時(shí)(無論是在瀏覽器中還是在 XamlPad 中),顯示的似乎不只是文檔本身,還會顯示其他少量內(nèi)容。特別是,文檔底部會呈現(xiàn)一些控件。如圖?8?所示,流文檔默認(rèn)會通過 FlowDocumentReader 控件呈現(xiàn),它提供了一組標(biāo)準(zhǔn)功能,例如縮放、分頁、不同視圖模式切換,甚至查找功能。事實(shí)上,流文檔需要由一些能夠顯示它們的某類控件承載。流文檔的默認(rèn)查看器是 FlowDocumentReader 控件,除非您明確使用其他控件,否則該控件會自動(dòng)實(shí)例化。WPF 目前提供三個(gè)不同的控件用于查看流文檔:
圖 8?FlowDocumentReader 控件中的控件按鈕 (單擊該圖像獲得較大視圖)
FlowDocumentScrollViewer? 此控件使用一個(gè)滾動(dòng)條以連續(xù)的流顯示文檔,類似網(wǎng)頁或 Microsoft Word 中的“Web 版式”。圖?9?顯示的是滾動(dòng)查看器中的文檔。
圖 9?使用 FlowDocumentScrollViewer 控件 (單擊該圖像獲得較大視圖)
FlowDocumentPageViewer? 此控件以單獨(dú)的頁面顯示流文檔,讓頁面翻轉(zhuǎn)而非滾動(dòng)。這與 Word 中的“閱讀版式”類似。圖?10?顯示的是頁面查看器。在這里,圖?9?中的文檔使用 FlowDocumentPageViewer 控件呈現(xiàn),滾動(dòng)條被分頁機(jī)制取代。這種簡單的流布局方法已被一種更高級、多列的分頁布局所取代。
圖 10?使用 FlowDocumentPageViewer 控件 (單擊該圖像獲得較大視圖)
FlowDocumentReader? 此控件組合了滾動(dòng)查看器和頁面查看器,讓用戶可以在兩種方法之間切換。這是用于流文檔的默認(rèn)控件,而且對于以顯示復(fù)雜文本為特色的應(yīng)用程序通常是一個(gè)不錯(cuò)的選擇。在圖?11?中,圖?9?和圖?10?中顯示的同一文檔通過 FlowDocumentReader 呈現(xiàn),它將滾動(dòng)查看器和頁面查看器兩種方法結(jié)合在一起。此外,它還啟用了其他控件中默認(rèn)隱藏的搜索功能(其他查看器的確支持查找功能,通過執(zhí)行 ApplicationCommands.Find 命令或從鍵盤上按 Ctrl+F 可實(shí)現(xiàn)該功能)。讀取器控件還支持多頁視圖,這稍微改變了基于頁面的呈現(xiàn),以及列和圖的呈現(xiàn)方式。
圖 11?使用 FlowDocumentReader 控件 (單擊該圖像獲得較大視圖)
雖然 FlowDocumentReader 幾乎對所有基本使用情形都很有吸引力,但選擇怎樣的控件還需視您的情況而定。它用途廣泛且功能強(qiáng)大,并支持分頁布局,這在許多情形下是比滾動(dòng)更高級的功能。關(guān)于該主題的更詳細(xì)討論不在本文探討范圍之內(nèi),但事實(shí)證明,滾動(dòng)及重合等相關(guān)效果是人們較之?dāng)?shù)字化文本更喜歡打印文本的主要原因之一。分頁方法在許多情況下更為自然,有助于讓數(shù)字化閱讀被更普遍接受。
那么您如何定義要使用哪個(gè)控件呢?一個(gè)簡單但相當(dāng)強(qiáng)力的方法是將想要的控件添加到文檔的 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 lazydog.</Paragraph></FlowDocument> </FlowDocumentScrollViewer>?
在本例中,文檔根已被設(shè)為一個(gè) FlowDocumentScrollViewer 標(biāo)記。也就是說,您不再只是定義一個(gè)單純的文檔而已。相反,您在定義一個(gè)完整的 XAML 界面,而它碰巧使用滾動(dòng)查看器作為其根。滾動(dòng)查看器的內(nèi)容是最開始示例中的流文檔。(請注意,命名空間定義現(xiàn)在使用滾動(dòng)查看器標(biāo)記,而非流文檔標(biāo)記)。圖 9 到圖 11 是使用此方法創(chuàng)建的,不同的查看器控件用作根元素。
我為何把這稱為強(qiáng)力方法呢?這是因?yàn)?#xff0c;從結(jié)構(gòu)角度看,將用戶界面定義與其實(shí)際數(shù)據(jù)相混合會導(dǎo)致一些問題。而更理想的狀況是將文檔與其界面分開。將讀取器與文檔混合在一起有點(diǎn)像創(chuàng)建一個(gè) SQL Server? 表,并出于某種原因定義該表只能在 Windows Forms DataGrid 中顯示。有若干方法可讓文檔與 UI 定義分離。如果想使用上文所示的 ASP.NET 方法將流文檔作為 Web 應(yīng)用程序的一部分顯示,您可使用所需的查看器控件定義 ASP.NET 頁面,然后只要使用標(biāo)準(zhǔn) ASP.NET 代碼合并到實(shí)際內(nèi)容(單獨(dú)存儲,可能在數(shù)據(jù)庫中)即可。
另一方面,在一個(gè)典型的 WPF 應(yīng)用程序中,您可以只要使用標(biāo)準(zhǔn) WPF、Windows 和 XAML 瀏覽器應(yīng)用程序 (XBAP) 方法來定義您的用戶界面,然后動(dòng)態(tài)加載您的文檔即可。圖?12顯示的是使用我文章中的一個(gè)虛構(gòu)庫的一個(gè)簡單示例,這些文章顯示在左上角的一個(gè)列表框中。用戶從列表中選擇一篇文章時(shí),該文檔會自動(dòng)加載到占用大部分窗體的 Flow Document Reader 控件。請注意,諸如 alpha 值混合處理等標(biāo)準(zhǔn) WPF 技術(shù)在此設(shè)置中也能使用。您會注意到,實(shí)際的流文檔是半透明的,背景中我的照片也在閃爍。另外也請注意,應(yīng)用程序使用了一個(gè)列表框、圖像,一個(gè)標(biāo)簽和一個(gè) FlowDocumentReader 控件來創(chuàng)建虛構(gòu)文章的庫。
圖 12?使用列表框、圖像、標(biāo)簽和 FlowDocumentReader 控件 (單擊該圖像獲得較大視圖)
這個(gè)例子最棘手的地方是將實(shí)際文檔加載到查看器控件中。這通過 System.Windows.Markup.XamlReader 類實(shí)現(xiàn),它允許動(dòng)態(tài)加載任何 XAML 內(nèi)容,包括但不限于流文檔。以下是我綁定到列表框選定更改事件的一行代碼:
?
documentReader.Document = (FlowDocument)XamlReader.Load( File.OpenRead(fileName));?
Load 方法會返回一個(gè)對象,因?yàn)?XAML 文件中的根元素可以代表許多不同類型。在我的例子中,我知道返回值為 FlowDocument,因此我只要執(zhí)行一個(gè)轉(zhuǎn)換,并將該文檔指定給 FlowDocumentReader 控件的 Document 屬性即可(此例中,我將控件實(shí)例命名為 documentReader)。請記住,這只是個(gè)例子。生產(chǎn)品質(zhì)的代碼此處當(dāng)然還需要一些錯(cuò)誤處理。
請注意,您了解的關(guān)于 WPF 的所有東西都適用于本例。例如,讀取器控件只是支持樣式的標(biāo)準(zhǔn) WPF 控件。也就是說,您可以完全更改所有 UI 元素的外觀,例如縮放欄、視圖模式切換或分頁控件。(您的控制能力受到限制的唯一元素是搜索框,雖然如果您不喜歡它,就根本不必用它。)
此外,我的例子顯示的是基于 Windows 的應(yīng)用程序,相同的應(yīng)用程序也可以作為 XBAP 部署,并在 Web 瀏覽器內(nèi)運(yùn)行(當(dāng)然,我們還是假設(shè)用戶已安裝了 .NET Framework 3.0)。請注意,Microsoft Silverlight?(原代號為“WPF/E”)是不夠的,因?yàn)?Silverlight 只支持 WPF 的子集,且并不支持流文檔。
創(chuàng)建流文檔
如何編寫流文檔?當(dāng)然,開發(fā)人員始終可以使用諸如 XamlPad 等低級工具來編寫流文檔。但是,在現(xiàn)實(shí)環(huán)境下,這不大可能。通常,流文檔是使用 WYSIWYG 編輯器或通過從現(xiàn)有文檔格式進(jìn)行的內(nèi)容轉(zhuǎn)換來創(chuàng)建的。由于流文檔可以使用 XAML 定義,因此轉(zhuǎn)換現(xiàn)有 XML 內(nèi)容特別簡單。但也可以轉(zhuǎn)換 HTML 和 Word 文檔,而無需付出過大的精力(盡管需要編碼,因?yàn)槠駷橹股形闯霈F(xiàn)現(xiàn)成工具)。
對于 WYSIWYG 編輯,WPF 提供了一個(gè)現(xiàn)成的控件。WPF RichTextBox 控件可以本機(jī)編輯 XAML 流文檔。該控件名稱讓人誤以為它是專門針對 RTF 格式。盡管這個(gè)控件也支持 RTF,但實(shí)際上它主要用于流文檔。事實(shí)上,該控件實(shí)際上會反映流文檔查看控件,只不過它也支持編輯。有些人甚至?xí)f,RichTextBox 控件應(yīng)該被視為顯示流文檔的另一種方式。
將下列示例鍵入 XamlPad 中,以查看運(yùn)行中的 RichTextBox 控件:
?
<RichTextBoxxmlns=’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 也有一個(gè) Document 屬性,您可以自動(dòng)以此會話中的流文檔填充其值。這實(shí)際上會創(chuàng)建一個(gè)與 FlowDocumentScrollViewer 控件看起來很相似的 UI,只不過其中的文本可以編輯。請注意,此文本框控件始終以滾動(dòng)方式處理流文檔。在分頁或多列模式下,無法在 RichTextBox 中編輯流文檔。不過,編輯操作的結(jié)果是一個(gè)標(biāo)準(zhǔn)流文檔,該文檔可以使用您已看到的任何一種查看器機(jī)制顯示,其中包括多列和分頁模式。
關(guān)于 RichTextBox,值得一提的其中一項(xiàng)功能是集成的拼寫檢查。您可以按如下所示啟用該功能:
?
<RichTextBox SpellCheck.IsEnabled=”true”><FlowDocument>...</FlowDocument> </RichTextBox>?
圖?13?顯示了運(yùn)行中的拼寫檢查程序。
圖 13?帶有拼寫檢查功能的 RichTextBox 控件 (單擊該圖像獲得較大視圖)
使用此控件唯一復(fù)雜的地方是加載與保存。在許多情形下,您可能不會像在之前的例子中那樣,將 RichTextBox 內(nèi)容編碼到 UI XAML 中,而是要?jiǎng)討B(tài)加載和保存文檔。RichTextBox 中文本的加載操作與為查看器控件加載流文檔相同(見上文)。保存文檔本質(zhì)上則完全相反:您要先拿到文檔對象,然后將其序列化回 XAML,如下所示:
?
System.Windows.Markup.XamlWriter.Save(richTextBox.Document)?
這會將 XAML 作為一個(gè)字符串返回,然后您可以將其存儲到文件或數(shù)據(jù)庫中,或者使用您能想到的任何其他方式。
RichTextBox 非常方便,不過在這里還是要提醒幾句話。雖然流文檔代表了可用于呈現(xiàn)屏幕文檔的最復(fù)雜的技術(shù),但 RichTextBox 控件卻一點(diǎn)也不復(fù)雜。它是編輯小型文檔和文本段的極佳選擇,但是您不會用它來編寫書籍、雜志或營銷手冊。對于這些長格式,它的呈現(xiàn)過于簡單,因?yàn)樗恢С殖凉L動(dòng)布局之外的其他任何布局(也就是說,還沒有一種很好的可視方式可用于創(chuàng)建我稍后將談到的高級布局)。同樣,用于保存文檔的方法也經(jīng)常不盡人意。XmlWriter 類只是使用實(shí)時(shí)的內(nèi)存中文檔,并將其轉(zhuǎn)換為 XAML,但遺憾的是,對于大規(guī)模的流文檔操作非常重要的許多概念(例如樣式),它并未注意。結(jié)果,盡管 XAML 忠實(shí)地保存了文檔的外觀,但文檔看起來往往不太清爽,并且很大。RichTextBox 控件當(dāng)然還是很有用的,但是別指望將它作為屏幕內(nèi)容的桌面出版解決方案(雖然這類應(yīng)用程序非常急需)。
探究布局可能性
至此您已了解了如何編寫和查看流文檔,接著讓我們回到文檔本身,看看更多的功能。流文檔非常復(fù)雜,探究所有可用功能超出了本文范圍,不過我想再討論幾項(xiàng)功能。
其中一項(xiàng)一直讓我著迷的功能是“最佳段落”。啟用該功能后,可以在指定段落內(nèi)盡可能平均地分布空白,從而帶來顯著改進(jìn)的閱讀體驗(yàn)。“最佳段落”特別適合與另一項(xiàng)內(nèi)置功能“斷字”搭配使用,該功能(居然)會執(zhí)行動(dòng)態(tài)整個(gè)流文檔或者個(gè)別段落的斷字。
啟用最佳段落和斷字功能是項(xiàng)非常簡單的操作:
?
<FlowDocument IsOptimalParagraphEnabled=”true” IsHyphenationEnabled=”true”>?
圖?14?顯示的是相同的文檔,只是呈現(xiàn)時(shí)啟用或禁用了這些功能。兩個(gè)版本間的區(qū)別非常細(xì)微,但是非常重要。請注意,左邊的版本看起來更平和,主要因?yàn)樵~與詞之間的空白分布得更平均,且從整體上減少了。特別是在屏幕上閱讀大量文本時(shí),這個(gè)看起來細(xì)小的區(qū)別會變得極為重要。
圖 14?最佳段落和斷字 (單擊該圖像獲得較大視圖)
如您所見,FlowDocumentReader 控件采取多列的方法呈現(xiàn)文本。這是另一項(xiàng)非常重要的可讀性功能,因?yàn)槿藗儾幌矚g讀跨越整個(gè)寬屏顯示頁面寬度的一行行文字。實(shí)際列寬因各種因素會有所不同,例如用于內(nèi)容顯示的可用總寬度、縮放系數(shù)和定義的列寬等。流文檔的默認(rèn)列寬為字體大小的 20 倍,默認(rèn)字體大小約為 300 個(gè)與設(shè)備無關(guān)的像素(3 1/8 英寸的精確尺寸顯示)。您可以很輕松地覆蓋此默認(rèn)設(shè)置:
?
<FlowDocument ColumnWidth=”400”>?
這會產(chǎn)生寬度約 400 像素的列。不過,還有其他一些因素會影響實(shí)際寬度。舉例來說,如果縮放比例是 50%,那么實(shí)際列寬就只有 200 像素。另外,到目前為止,列寬更多地會被看作最小列寬。這意味著,如果可用總寬度為 900 像素,要呈現(xiàn)結(jié)果包含兩列,并且要充分填滿這整個(gè) 900 像素的話,就要讓每列的寬度都超過定義的 400 像素。通常都需要這樣,因?yàn)樗鼤尦尸F(xiàn)結(jié)果看起來非常美觀。不過,如果您不想執(zhí)行該行為,而只希望列寬實(shí)際就是 400 像素的話,可以確保列寬不是靈活可變的:
?
<FlowDocument ColumnWidth=”400” IsColumnWidthFlexible=”false”>?
現(xiàn)在,所有列都正好是 400 像素(100% 縮放),剩余空間就讓它顯示為空白。
另一個(gè)您可能想嘗試的與列相關(guān)的設(shè)置是列之間的空隙。這可以通過 ColumnGap 屬性調(diào)整(此設(shè)置也是基于與設(shè)備無關(guān)的像素?cái)?shù)):
?
<FlowDocument ColumnGap=”25”>?
其中一個(gè)相關(guān)的設(shè)置是列規(guī)則,它允許在列之間定義一個(gè)可視元素。請看此例(其結(jié)果見圖?15):
圖 15?列之間采用簡單規(guī)則的流文檔 (單擊該圖像獲得較大視圖)
?
<FlowDocument ColumnRuleWidth=”5” ColumnRuleBrush=”Red”>?
當(dāng)然,在許多出版物中,文檔并不只是采用簡單的列布局。通常還存在從一般流中提取出來的其他元素。您已見過這樣的例子,例如將圖像置于文檔中。圖?12?顯示了圖形設(shè)計(jì)師常用的一種排列方式。此圖像位于兩列之間,周圍環(huán)繞文字,圖像方方正正地位于內(nèi)容中間,并沒有影響任何一列的文字布局。這是一種常見的布局選擇,只是還不能用于流文檔之前我所了解的動(dòng)態(tài)屏幕閱讀環(huán)境。
創(chuàng)建此類布局的關(guān)鍵是圖形塊,它允許定義不與文檔其余部分那樣布局的內(nèi)容。將圖像置于圖形標(biāo)記內(nèi)部就是一例,但圖形還有許多其他用途。例如,您可以使用圖形來定義橫跨整個(gè)文檔寬度的標(biāo)題:
?
<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 setof features. </Paragraph>?
在本代碼中,圖形包含另一個(gè)段落,即用作標(biāo)題的文本。請注意,這里有一些您可用來創(chuàng)建高級、靈活文檔的便捷屬性。例如,看一下圖形的寬度。我沒有將寬度設(shè)為特定像素?cái)?shù),而是將其設(shè)為內(nèi)容的確切寬度,這會根據(jù)整個(gè)內(nèi)容的寬度自動(dòng)調(diào)整圖形寬度。
請看圖?16。其中,您會注意到標(biāo)題(通過圖形放置)設(shè)為橫跨整個(gè)內(nèi)容寬度,這就將所有四列的位置都向下推移了。該圖像從垂直和水平方向看都定位于頁面中央。
圖 16?標(biāo)題橫跨四列 (單擊該圖像獲得較大視圖)
請注意,其寬度與內(nèi)容相關(guān)的圖形不必始終與內(nèi)容一樣寬。以下例來說,圖形寬度設(shè)為內(nèi)容寬度的 75%:
?
<Figure Width=”0.75Content”>?
寬度也可與其他項(xiàng)相關(guān),例如列寬。下例圖形始終是兩列寬(除非只顯示一列,那樣寬度就會減為一列):
?
<Figure Width=”2Column”>?
當(dāng)然,圖形高度可通過類似方式定義(雖然圖形通常是隨著內(nèi)容縱向變化)。
另一重要方面是圖形的位置。在代碼段中,它設(shè)為橫向定位為靠左,縱向定位為靠上。也就是說,圖形會出現(xiàn)在當(dāng)前內(nèi)容頁面的左上角,而無論其實(shí)際如何定義。然而在本示例中,圖形被定義為文檔的第一個(gè)元素,但即使該標(biāo)題之前已有段落,它也會由于這些設(shè)置而被上移和左移。圖?12?和圖?16?中的照片已按類似方式,將其橫向定位為“PageCenter”,在列之間移動(dòng)。(所有這些設(shè)置的可用屬性值都可以在 WPF 文檔中找到)。
您可能已經(jīng)注意到,本文涉及了大量手動(dòng)編碼。例如,每當(dāng)需要改變字體時(shí),您都要將該信息添加到塊或內(nèi)嵌元素中。到目前為止,這還不是一個(gè)大問題,因?yàn)榇蟛糠质纠己苄 5?#xff0c;如果有一本每 50 頁為一章的書,您要改變每一段的字體,每次都手動(dòng)來改的話,無疑會很繁重。幸運(yùn)的是,現(xiàn)在有了一個(gè)更好的辦法:如 WPF 中的其他任何內(nèi)容一樣,流文檔支持樣式。樣式可被定義為實(shí)際流文檔中指定名稱的資源。以下是定義字體信息的樣式:
?
<FlowDocument><FlowDocument.Resources><Style x:Key=”MyStyle”><Setter Property=”TextElement.FontSize” Value=”12” /><Setter Property=”TextElement.FontFamily” Value=”Bodoni MT” /></Style><FlowDocument.Resources>... </FlowDocument>?
然后,該樣式會通過以下方式應(yīng)用到段落(和其他元素):
?
<Paragraph Style=”{StaticResource MyStyle}”>The quick... </Paragraph>?
由于流文檔的特性,樣式特別常用。建議您對于最簡單情形之外的任何情形,都使用樣式來定義大部分格式選項(xiàng),而不是通過個(gè)別內(nèi)嵌元素的屬性。樣式可讓您的文檔保持緊湊,而且更易維護(hù)。
總結(jié)
希望本文不只讓您獲得對流文檔及其功能的基本了解,而且也激發(fā)起您的興趣。還有許多更高級的功能,包括查看器控件的復(fù)雜樣式、子類化和延伸文檔、塊和內(nèi)嵌元素、數(shù)字權(quán)限管理、文本和墨跡注釋功能以及高級字體格式等,絕對值得您深入研究。
轉(zhuǎn)載于:https://www.cnblogs.com/jiewei915/archive/2012/09/23/2699079.html
總結(jié)
以上是生活随笔為你收集整理的WPF中使用流文档灵活地显示内容的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 查询性能优化——创
- 下一篇: 词频统计工程相关