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)。
當然,這是一個非常簡單的例子,文檔的定義和嵌入式布局會復雜得多。流文檔支持您能想到的所有格式,例如斜體、下划線、字體顏色和字體等。圖 2 顯示的是一個稍微高級的示例,其結果可在圖 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顯示了與此類似的一個文檔的呈現。
現在,您已了解如何創建一些簡單的流文檔以及如何在 XamlPad 中查看它們。而目前我所忽略的是該如何在自然狀態下查看流文檔。畢竟,您不會期望用戶打開 XamlPad,然后粘貼文檔的 XAML。查看 XAML 流文檔的一種方法是將其另存為一個擴展名為 .xaml 的文件,然后在 Windows 資源管理器中雙擊它。這會啟動與 XAML 文件相關聯的默認應用程序(通常是 Internet Explorer®),從而顯示該文檔。結果如圖 6 所示。
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 目前提供三個不同的控件用於查看流文檔:
FlowDocumentScrollViewer 此控件使用一個滾動條以連續的流顯示文檔,類似網頁或 Microsoft Word 中的“Web 版式”。圖 9 顯示的是滾動查看器中的文檔。
FlowDocumentPageViewer 此控件以單獨的頁面顯示流文檔,讓頁面翻轉而非滾動。這與 Word 中的“閱讀版式”類似。圖 10 顯示的是頁面查看器。在這里,圖 9 中的文檔使用 FlowDocumentPageViewer 控件呈現,滾動條被分頁機制取代。這種簡單的流布局方法已被一種更高級、多列的分頁布局所取代。
FlowDocumentReader 此控件組合了滾動查看器和頁面查看器,讓用戶可以在兩種方法之間切換。這是用於流文檔的默認控件,而且對於以顯示復雜文本為特色的應用程序通常是一個不錯的選擇。在圖 11 中,圖 9 和圖 10 中顯示的同一文檔通過 FlowDocumentReader 呈現,它將滾動查看器和頁面查看器兩種方法結合在一起。此外,它還啟用了其他控件中默認隱藏的搜索功能(其他查看器的確支持查找功能,通過執行 ApplicationCommands.Find 命令或從鍵盤上按 Ctrl+F 可實現該功能)。讀取器控件還支持多頁視圖,這稍微改變了基於頁面的呈現,以及列和圖的呈現方式。
雖然 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 控件來創建虛構文章的庫。
這個例子最棘手的地方是將實際文檔加載到查看器控件中。這通過 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 顯示了運行中的拼寫檢查程序。
使用此控件唯一復雜的地方是加載與保存。在許多情形下,您可能不會像在之前的例子中那樣,將 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 顯示的是相同的文檔,只是呈現時啟用或禁用了這些功能。兩個版本間的區別非常細微,但是非常重要。請注意,左邊的版本看起來更平和,主要因為詞與詞之間的空白分布得更平均,且從整體上減少了。特別是在屏幕上閱讀大量文本時,這個看起來細小的區別會變得極為重要。
如您所見,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):
<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。其中,您會注意到標題(通過圖形放置)設為橫跨整個內容寬度,這就將所有四列的位置都向下推移了。該圖像從垂直和水平方向看都定位於頁面中央。
請注意,其寬度與內容相關的圖形不必始終與內容一樣寬。以下例來說,圖形寬度設為內容寬度的 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>
由於流文檔的特性,樣式特別常用。建議您對於最簡單情形之外的任何情形,都使用樣式來定義大部分格式選項,而不是通過個別內嵌元素的屬性。樣式可讓您的文檔保持緊湊,而且更易維護。
希望本文不只讓您獲得對流文檔及其功能的基本了解,而且也激發起您的興趣。還有許多更高級的功能,包括查看器控件的復雜樣式、子類化和延伸文檔、塊和內嵌元素、數字權限管理、文本和墨跡注釋功能以及高級字體格式等,絕對值得您深入研究。













