程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《Programming WPF》翻譯 第7章 1.圖形基礎

《Programming WPF》翻譯 第7章 1.圖形基礎

編輯:關於.NET

WPF使得在你的應用程序中使用圖形很容易,以及更容易開發你的顯卡的能力 。這有很多圖形構架的方面來達到這個目標。其中最重要的是綜合。

7.1.1綜合

圖形化元素可以組合到你的用戶界面的任何一部分中。很多GUI技術易於將圖 形分離到一個獨立的世界。這就需要一個“操縱桿”——當移動一個世界的按鈕 、文本框和其它框架到另一個世界的Shape和圖像中,由於在很多系統中,這兩 個世界有不同的編程模型。

例如,Windows Forms和Mac OS的Cocoa都提供了在窗體中排列控件的能力, 以及建立一個與這些控件交互的程序。它們還提供了一些API,這些API提供了高 級的、完全的可伸縮的2維空間繪圖工具。(在Windows Forms中為GDI+,在OS X 中為Quartz 2D。)但是這些繪圖API是不同於控件API的。繪圖基礎是非常不同 於這些系統中的控件的,你不可以自由的混合這兩種。

WPF,另一方面,把Shape作為UI樹中的元素,就像其他元素一樣。因此,我 們可以自由的混和它們在任意類型的元素中。示例7-1顯示了各種各樣的例子。

示例7-1

<DockPanel>
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
        <TextBlock>Mix</TextBlock>
        <Rectangle Fill="Red" Width="20" />
        <TextBlock>text</TextBlock>
        <Ellipse Fill="Blue" Width="40" />
        <TextBlock>and</TextBlock>
        <Button>Controls</Button>
    </StackPanel>
    <Ellipse DockPanel.Dock="Left" Fill="Green" Width="100" />
    <Button DockPanel.Dock="Left">Foo</Button>
    <TextBlock FontSize="24" TextWrap="Wrap">
        And of course you can put graphics into
        your text: <Ellipse Fill="Cyan" Width="50" Height="20" />
    </TextBlock>
</DockPanel>

正如你看到的,圖形化元素可以被無縫隙的添加到標記中。外觀和圖形一起 工作——正如它和任何其它元素一起工作一樣,結果如圖7-1所示。

圖7-1

雖然這個例子是在xaml中,你還可以使用代碼創建元素。本章的大多數示例 都使用xaml,因為標記的結構直接反射了被創建對象的結構。然而,究竟使用代 碼還是標記依賴於你正在做什麼。如果你是在創建繪圖,你多數會使用一個xaml 來為這些繪圖創建xaml,但是如果你從數據中創建圖形,從代碼中做些事情就是 有意義的。本章顯示的大多數技術可以使用在代碼或標記中。參見附錄A獲取更 多XAML和代碼間關系的信息。

不僅圖形和其它內容可以在標記中並排放置在一起,它們還可以混合在一起 。注意到圖7-1,右手邊的橢圓,被放置在TextBlock中。如果你想達到在 WindowsForms中的排列效果,只使用Label控件是不夠的,你將不得不寫一個新 的控件,從頭開始繪制這些文本和橢圓。這種混合有兩種方式,不僅你可以把你 的控件混合到你的圖形中,還可以使用圖形化元素來自定義你的控件外觀——如 第五章所描述的。

這種混合不只是像文本塊這樣的簡單元素,同時也適用於控件。例如,圖7- 2顯示了一個按鈕,其中混合了文本和圖形作為標題。

圖7-2

傳統的Windows中,你可以得到這種效果,以來於按鈕可以被顯示為一張圖片 。但是圖片是相當沒有彈性的,它們只是一個混合了圖片的塊,因此你不能容易 的使圖片的一部分產生交互或使選中的部分響應用戶輸入以產生動畫。因此,在 WPF中,把圖形放入按鈕,這種方式工作的有點不一樣。按鈕的標記顯示在示例 7-2中。

示例7-2

<Button>
    <StackPanel Orientation="Horizontal">
        <Canvas Width="20" Height="18" VerticalAlignment="Center">
            <Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
                        Fill="Yellow" Stroke="Black" />
            <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" />
        </Canvas>
        <TextBlock VerticalAlignment="Center">Click! </TextBlock>
    </StackPanel>
</Button>

當然,帶有圖像的按鈕並不是一個新事物,但是傳統上,按鈕允許設置一張 圖片來支持於此。例如,Windows Forms中,按鈕有一個Image屬性,以及在 Cocoa中,NSButton有一個setImage方法。這種實現使相當沒有彈性的——控件 允許設置一個單獨的標題和一張單獨的圖片。比較示例7-2,這裡使用了 StackPanel在按鈕內部布局以及只增加它需要的內容。你可以使用任意的外觀面 板在Button中,帶有任意類型的內容。示例7-3使用了一個Grid來排列文本和一 些橢圓在Button中。結果如圖7-3所示。

示例7-3

<Button HorizontalAlignment="Center" VerticalAlignment="Center">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Ellipse Grid.Column="0" Grid.Row="0" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="2" Grid.Row="0" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="0" Grid.Row="2" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="2" Grid.Row="2" Fill="Blue" Width="10" Height="10" />

        <Ellipse Grid.ColumnSpan="3" Grid.RowSpan="3" Stroke="LightGreen"
 StrokeThickness="3" />

        <TextBlock Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">Click! </
 TextBlock>
    </Grid>
</Button>

圖7-3

在WPF中,很少需要提供非彈性屬性如Text或Image的元素。如果一個表示內 嵌內容的元素是有意義的,那麼它將會表示無論什麼你選擇提供的混合型元素。

如果你熟悉二維繪圖技術如Quartz 2D,GDI+或者舊式的GDI32,另一種高級 的繪圖方式會使你震驚。我們不再需要寫一個函數來響應重畫的請求。WPF可以 為我們保持屏幕重畫。這是因為WPF使我們表示將圖形表示為對象。

7.1.2繪圖對象模型

有很多GUO技術,想自定義外觀的應用程序,需要能夠在一開始就創建它們的 外觀。通常顯示一個自定義的外觀的技術是寫代碼來表現一系列繪圖操作,從而 創建顯示。這些代碼將會在相應的圖形第一次需要顯示的時候運行。在一些系統 中,OS不會保留應用程序繪圖的副本,因此改方法會以運行時每次需要重畫某個 區域而告終,例如,如果一個窗體是不透明而且沒有被蓋住的。

在使用這個方法時,在這個生成函數構建整張圖片時,更新獨立的元素通常 是有問題的。即使是OS獲得繪圖的一份副本的地方,通常是獲取到一個圖像。這 意味這一旦你想要改變繪圖的一部分,你通常需要重畫改變區域的每一個事物。

WPF提供了一種不同的方式:你可以添加表示圖形化形狀的對象到用戶元素樹 上。形狀元素是UI樹中和其它元素一樣的對象,因此你的代碼可以在任何時刻修 改它們。如果你改變某個有可視化效果的屬性,如Size,Location,或Color, WPF將會自動更新顯示。

為了說明這個技術,示例7-4顯示了一個簡單的包含一些橢圓的窗體。其中每 個橢圓都由一個Ellipse對象表示,我們將在後台代碼文件中更新顯示。

示例7-4

<Window x:Class="ChangeItem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    Text="Change Item"
    >
    <Canvas>
        <Ellipse Canvas.Left="10" Canvas.Top="30" Fill="Indigo"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="20" Canvas.Top="40" Fill="Blue"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="30" Canvas.Top="50" Fill="Cyan"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="40" Canvas.Top="60" Fill="LightGreen"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="50" Canvas.Top="70" Fill="Yellow"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
    </Canvas>
</Window>

每個橢圓的MouseLeftButtonDown事件都由定義在該窗體對應後台文件的 OnClick方法處理。如示例7-5所示,OnClick方法簡單的增加了Width屬性——當 Ellipse激發這個事件的時候。點擊的結果使這個橢圓更加寬了。

示例7-5

using System.Windows;
using System.Windows.Shapes;

namespace ChangeItem
{
    public partial class MainWindow : Window
    {
        public MainWindow( ) : base( )
        {
            InitializeComponent( );
        }


        private void OnClick(object sender, RoutedEventArgs e)
        {
            Ellipse r = (Ellipse) sender;
            r.Width += 10;
        }
    }
}

如果我們使用舊的方法在一個單獨的生成函數中繪制每一個事物,這樣的代 碼對於更新顯示不充分地。正規地,這將告訴OS,屏幕不再是有效地,導致激發 一個重畫請求。但是在WPF中,這是不必要的——當你在Ellipse對象上設置一個 屬性時,WPF保證了屏幕會相應地更新。進一步說,WPF知道所有這些交疊項,正 如圖7-4所示,因此,為了獲得正確的結果,它將會根據需要上上下下地重繪這 些項。所有你必須去做的時調整這個對象的屬性。

圖7-4

由於GUI第一次開始呈現,盡管計算機內存容量已經成數量級的增長,仍然有 一些情形,使得繪制這個對象模型的方法可能會非常昂貴。尤其是,因為應用程 序處理的是巨大的數據集,如maps,在UI樹中有一組完全的對象,鏡像映射了底 層數據的機構,可能使用太多的內存。用於,為了某中類型的圖形或數據,它可 能更便利的使用舊有的生成代碼的樣式。為此,WPF還支持一些輕量級的操作模 式。這將在本章的後面進行介紹——在“可視化層次編程”一節。

你可能已經注意到,到目前為止,我們已經做的所有的圖畫都使用形狀而不 是圖片,WPF當然支持圖片,但存在一個好的使用形狀的原因。幾何形狀可以被 伸縮和旋轉而不損失圖片質量。高質量的變型是WPF繪圖中的一個重要特征。

7.1.3分辨率的獨立性

當GUI第一次顯示時,不僅顯示卡可以顯著的提高性能,顯示器也可以。長期 以來,唯一的主流顯示技術是CRT。CRT的顏色只提供了有限的分辨率。它們努力 顯示清晰度高於100px/I的圖片。然而,純平顯示器,現在賣的比CRT多,可以在 很大的余地勝過它。

本書的作者之一有一台使用兩年的膝上型電腦,分辨率為150px/I。寫作本書 的時候,顯示器可利用超過了200px/I。創建更高象素密度是可能的。盡管如此 ,在當前操作系統使用這些顯示器仍然存在一個問題。每個事物都會以很小以至 於不可用而告終。這是因為一種基於象素的開發文化,大多數應用程序使用象素 按量配給它們的用戶界面。

這並不是完全的技術限制的結果。從Wndows NT使用到現在,以一種獨立於分 辨率的方式繪圖是可能的,因為繪圖API和GDI32運行你應用變型到所有你的繪圖 中。在2001引進的GDI+,提供了同樣的機制。但是正因為有效的樣式並不意味著 應用程序就可以使用它。大多數應用程序並不暴露它的可伸縮性。

不幸的是,在Win32中,將圖形和其他UI元素分隔開意味著,即使應用程序沒 有暴露繪圖API中的可伸縮性,其他UI部分不會自動地遵循。圖7-5顯示了一個 Windows Forms應用程序,使用了GDI+來繪制圖形,可以任意的大小伸縮。

圖7-5

注意到在圖7-5中,雖然星星和“Hello World”文本已經被伸縮了, Trackbar和Label控件卻並沒有這樣。這是因為繪圖變型僅僅影響你使用GDI+繪 制的那些圖形。它們並不影訊整個UI。盡管Windows Forms提供了一些伸縮其余 UI元素的樣式,但這不是自動的。你不得不采用深思熟慮的非平凡的措施來建立 一個獨立於分辨率的UI在Windows Forms中。

7.1.3.1伸縮和旋轉

通過在底層支持變形,WPF解決了這樣一個問題。取代以只在二維繪圖級別提 供伸縮性,WPF將其生成在底層的合成的引擎中。結果是UI中的每一個事物都可 以被變形,不僅僅是用戶繪制的圖形。回到示例7-2我們簡單的帶有笑臉的按鈕 ,我們可以通過只改變第一行來使用這個伸縮性:

<Button LayoutTransform=”scale 3 3”>

LayoutTransform屬性在WPF所有的用戶接口元素中是有效的,因此你可以伸 縮整個窗體如同一個按鈕一樣簡單。多種類型的變形是可以利用的,這將在後面 更詳細的討論。目前,我們簡單地要求擴大一個按鈕——通過x和y方向上的因數 3。圖7-6顯示了擴大後地按鈕。

圖7-6

比較原始的圖7-2和圖7-6,後者比前者更明顯,正如你所希望的 。更加意味深長地,細節變得更零碎了。按鈕的圓角邊緣比它們在一個較小的版 本中更加容易看到。後者的形狀定義得更好。而且我們的圖形更加清晰。我們獲 取這種清晰度是因為WPF生成的按鈕看上去和詳細指明大小的按鈕一樣好。比較 圖7-6和圖7-7中的示例。

圖7-7

圖7-7顯示了發生了什麼——如果你簡單的擴大原始的小按鈕的圖 像。有很多種擴大圖像的不同方式。左邊的示例使用了簡單的算法,稱為“”, 有時又稱為“”。為了是這張圖片更大一些,象素被簡單的重復了。這導致了對 這張圖像一種很方正的感覺。右邊的示例使用了一種比較高級的插值算法。這使 得圓角看起來是圓的,同時不會遭受矮矮胖胖的象素效果,而這會導致看起來很 模糊。清晰,兩個圖像中沒有一個可以達到圖7-6的效果。

7.1.2分辨率,坐標系統,和“象素”

對伸縮圖形的支持,意味著在你的應用程序中使用的坐標系統和屏幕使用的 坐標系統間沒有固定的關聯。如果運行在一台高DPI的顯示器上,即使你不用伸 縮變形,變形會自動應用到你整個的應用程序上——這是真實的。

在WPF中,如果不是物理性象素,那麼默認的度量單位又是什麼呢?答案是, 有點混淆,象素。為了更加精確,真實的答案是,獨立於裝置的象素。

獨立於裝置的象素,WPF官方的定義是1/96每象素。如果你詳細指出一個形狀 的寬度為96px,就意味著這是1英寸寬。WPF會使用與所需同樣多的物理象素來填 充一英寸,例如,高分辨率的膝上型顯示器,分辨率達到150px/I。因此如果你 繪制一個寬為96px的象素,WPF將生成150px/I的寬度。

WPF依賴於系統級顯示器設置,來決定物理象素的大小。你可以通過Windows Display Properties程序來調整它們。右擊“桌面”,選擇“屬性”來顯示這個 程序,進下來進入到“設置”條。點擊“高級”按鈕,在打開的對話框中,選擇 “通用”條,這將告訴Windows你的顯示器分辨率。

你可能想知道為什麼WPF使用有點奇怪的選擇1/96英寸,以及為什麼叫 “pixel”。原因是,96dpi是Windows中默認的DPI——當運行時使用正常的字體 ,因此長期考慮pixel正常的大小。這意味著在帶有正常象素密度的顯示器上, WPF會伸縮你的繪圖為了它們保持正確的物理大小。

WPF為每一個伸縮優化圖形化樣式生成的能力,意味著它能理想的被用於利用 增長的顯示器分辨率。作為第一次,屏幕上的文字和圖形會競爭這些清楚的碎片 ——我們已經希望來自打印機的。當然,為了所有實際中的工作,我們需要一組 全面的繪圖基礎。

7.1.4形狀,筆刷和鋼筆

WPF繪圖工具中的大多數類被歸類為三種類型:形狀,筆刷和鋼筆。在這些主 題上有很多變體,我們將會在後面詳細檢查它們。然而,為了完全獲取任何地方 的圖形,對這些類一個基本的了解是強制的,

形狀是用戶界面樹中的對象,提供了用於繪圖的基本的創建塊。我們已經看 到的Ellipse、Path和Rectangle元素都是形狀對象的例子。同樣支持線條——基 本線條和多段線條,分別使用Line和PolyLine。還可以創建任意的填充形狀。 Polygon支持所有邊都是直的形狀,而一旦你需要弧形的邊,Path類支持填充如 同弧形一樣的形狀。圖7-8顯示了使用中的每一個形狀。

圖7-8

不管你選擇的是什麼圖形,你需要決定如何對其進行著色。為此 ,可以使用Brush。有很多可利用的Brush類型,最簡單的是單顏色的 SolidColorBrush。你可以使用LinearGradientBrush或RadialGradientBrush達 到更有趣的可視化效果。這些允許在界面的形狀上改變顏色,這是一種很好的辦 法——提供了很深的印象。你也可以基於圖像創建筆刷,ImageBrush使用了一個 圖片,DrawingBrush使用了一個可伸縮的繪圖。最後VisualBrush使你在任何可 視化樹、任何用戶界面中你喜歡和使用的大塊上繪制其他的形狀。這使得達到想 反射你的用戶界面的全部的效果很簡單。

最後,鋼筆用於繪制形狀的輪廓。鋼筆其實只是一個增強的筆刷。當你創建 一個Pen對象的時候,你給了它一個Brush來告訴它應該怎麼在屏幕上畫畫。Pen 類只是增加了一些信息如線條的厚度,陰影樣式。圖7-9顯示了一些可用的效果 ,使用了筆刷和鋼筆。

圖7-9

7.1.5合成

圖形構架的最後一個關鍵樣式是合成。在計算機圖形中,名詞“合成”是指 將多個形狀和圖片聯合在一起形成最後輸出的過程。WPF的合成模型是不同於傳 統Windows工作模型的,對於創建支持高質量可視化,這是至關緊要的。

在經典的Win32模型中,每個用戶界面元素都獨占它所擁有的應用程序窗體中 的某個區域。在每一個頂級窗體中,任何在窗體中給定的象素都完全由那一個確 切的元素控制。這就防止了元素是局部透明的。它同樣排除了在元素的邊緣使用 抗鋸齒處理,當聯合非矩形元素的時候,這個技術是尤其重要的。雖然各種各樣 的hack已經被分成提供幻想在Win32中的透明度,它們有局限以及可以有點便利 的與之工作。

WPF的組合模型支持任何形狀的元素以及允許它們交疊。它還允許元素有任意 局部或完全的透明化區域的混合。這意味著任何給定的屏幕上的象素都可能有多 個有作用的可視化元素。進一步,WPF使用鋸齒化處理在所有形狀的邊緣。這樣 減少了參差不齊的外觀,更簡單的繪圖技術可以在屏幕上生成,導致一個平滑外 觀的圖像。最後,組合引擎允許任何元素在組合前,將變形應用上去。

WPF的組合引擎使用了現代顯卡來加速繪圖的過程。在內,這可以在Direct3D 模型的頂層實現。這看起來是多余的,因為多半WPF的繪圖功能都是二維的,但 是大多數面向3維的功能,在現代顯卡上,可以還被用於繪制二維形狀。例如, 象素陰影可以被用於實現高級的ClearType機制,當組合文本到UI中。WPF開發了 同樣超快的多邊形繪圖機制——由SD游戲用來生成基本形狀。進一步,WPF在顯 卡上緩存了可視化樹的一部分,顯著的促進了重繪的性能——當在屏幕上改動小 的細節時。

現在讓我們看一下支撐WPF圖形系統的核心概念,讓我們仔細的看一下細節。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved