如今的軟體市場,競爭已經進入白熱化階段,功能強、運算快、界面友好、Bug少、價格低都已經成為了必備條件。這還不算完,隨著電腦的多媒體功能越來越強,軟體的界面是否色彩亮麗、是否能通過動畫、3D等效果是否吸引用戶的眼球也已經成為衡量軟體的標準。 軟體項目成功的三個要素是:資源、成本、時間。無論是為了在 ...
-
如今的軟體市場,競爭已經進入白熱化階段,功能強、運算快、界面友好、Bug少、價格低都已經成為了必備條件。這還不算完,隨著電腦的多媒體功能越來越強,軟體的界面是否色彩亮麗、是否能通過動畫、3D等效果是否吸引用戶的眼球也已經成為衡量軟體的標準。
軟體項目成功的三個要素是:資源、成本、時間。無論是為了在競爭中保持不敗還是為了激發起用戶對軟體的興趣,提高軟體界面的美化程度、恰當的將動畫和3D等效果引入應用程式都是一個必然趨勢。然而使用傳統的桌面應用程式開發工具和框架(如Winform、MFC、VB、Delphi等)進行開發時,為了使軟體界面變漂亮、加入動畫或者3D效果,邊際成本會非常的高。體現在:
資源消耗增大:需要招聘懂得動畫和3D編程的程式員,還需要更多的設計師、工薪和溝通成本隨著上升。
開發時間增加:界面美化、動畫和3D開發遠遠比業務邏輯開發困難、耗時。
成本增加:隨著資源消耗的增加和開發周期的拉長,成本必然增加。
之所以會出現這種情況,根本原因在於傳統開發工具和框架並沒有原生的支持美化用戶界面、嚮應用程式中添加動畫和3D效果等功能。舉個簡單的例子,當用戶提出需要把TextBox的外觀改成圓角時,Winform和Delphi程式員只能通過派生新類併在底層做修改的方法來實現。類似的用戶需求還有好多不得不實現,否則客戶會懷疑我們的開發能力;即使實現了也沒有什麼額外的經濟效益,因為這些東西在客戶的眼裡都是很簡單的東西。
WPF的推出可謂是對症下藥、專門解決上述問題。體現在:
XAML語言針對的是界面美化的問題,可以讓設計師直接加入開發團隊、降低溝通成本。
XAML的圖形繪製功能非常強大,可以輕易繪出複雜的圖標、圖畫。
WPF支持濾鏡功能,可以像PhotoShop一樣為對象添加各種效果。
WPF原生支持動畫開發,無論是設計師還是程式員,都能夠使用XAML或C#輕鬆開發製作絢麗的動畫效果。
WPF原生支持3D效果,甚至可以將其它3D建模工具創建的模型導進來、為我所用。
Blend作為專門的設計工具讓WPF如虎添翼,即能夠幫助不瞭解編程的設計師快速上手,又能夠幫助資深開發者快速建立圖形或者動畫的原型。
1.1 WPF繪圖
與傳統的.net開發使用GDI+進行繪圖不同,WPF擁有自己的一套繪圖API。使用這套API不但可以輕鬆繪製出精美的圖形,還可以為各種圖形添加類似與PhotoShop的“濾鏡效果”及“變形效果”。本節我們就一起研究WPF圖形API繪圖,效果和變形等功能。
先觀察下麵一組圖片:
顯然,這組圖片是矢量圖(Vector Image),無論怎樣放大縮小都不會出現鋸齒。你可能會想:“這是組PNG格式的圖片嗎?”答案是“NO”。這組圖是用XAML語言繪製的!XAML繪圖本身就是矢量的,而且支持各式各樣的填充和效果,甚至還可以添加濾鏡,這些功能絲毫不亞於Photoshop。以前,使用PhotoShop製作出來的圖形需要程式員使用.net的繪圖介面進行二次轉換才能應用到程式里,現在好了,直接把XAML代碼拿來用就可以了。
繪圖並不是VisualStudio的強項,這些漂亮的XAML矢量圖是怎麼畫出來的呢?答案是藉助Microsoft Expression Studio中的Blend和Design兩個工具。Blend我們已經介紹過了,用它可以直接繪製XAML圖形;Design可以像PhotoShop或者FireWorks那樣繪製圖形,再由設計者決定導出xaml格式還是png格式。雖然“唯代碼派”的程式員們在Visualstudio里一行一行寫代碼也能把複雜的圖形以非可視化的形式創建出來,但在Blend和Design中畫出原型再在Visual Studio裡面進行細節的修飾才是提高效率之道。
積沙成塔,集腋成裘,別看前面那些圖片很複雜,但都是由幾個有限的基本圖形組成的。WPF的基本圖形包括以下幾個(它們都是Shap類的派生類):
Line:直線段,可以設置其筆觸(Stroke)。
Rectangle:矩形,既有筆觸,又有填充(Fill)。
Ellipse:橢圓,長寬相等的橢圓即為正圓,既有筆觸又有填充。
Polygon:多邊形,由多條直線線段圍成的閉合區域,既有筆觸又有填充。
PolyLine:折線(不閉合),由多條首尾相接的直線組成。
Path:路徑(閉合區域),基本圖形中功能最強的一個,可由若幹直線,圓弧,被塞爾曲線組成。
1 直線
直線是最簡單的圖形。使用X1,Y1兩個屬性值可以設置它的起點坐標,X2,Y2兩個屬性值可以設置它的終點坐標。控制終點/起點做標就可以實現平行,交錯等效果。Stroke(筆觸)屬性的數據類型是Brush(畫刷),凡是Brush的派生類均可以用於給這個屬性賦值。因為WPF提供多種漸變色畫刷,所以畫直線也可以畫出漸變效果。同時,Line的一些屬性還可以幫助我們畫出虛線以及控制線段終點的形狀。下麵的例子綜合了這些屬性:
- <Window x:Class="WpfApplication1.Window45"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window45" Height="293" Width="437">
- <Grid>
- <Line X1="10" Y1="20" X2="260" Y2="20" Stroke="Red" StrokeThickness="10"></Line>
- <Line X1="10" Y1="40" X2="260" Y2="40" Stroke="Orange" StrokeThickness="6"></Line>
- <Line X1="10" Y1="60" X2="260" Y2="60" Stroke="Green" StrokeThickness="3"></Line>
- <Line X1="10" Y1="80" X2="260" Y2="80" Stroke="Purple" StrokeThickness="2"></Line>
- <Line X1="10" Y1="100" X2="260" Y2="100" Stroke="Black" StrokeThickness="1"></Line>
- <Line X1="10" Y1="120" X2="260" Y2="120" StrokeDashArray="3" Stroke="Black" StrokeThickness="1"></Line>
- <Line X1="10" Y1="140" X2="260" Y2="140" StrokeDashArray="5" Stroke="Black" StrokeThickness="1"></Line>
- <Line X1="10" X2="260" Y1="160" Y2="160" Stroke="Black" StrokeThickness="6" StrokeEndLineCap="Flat"></Line>
- <Line X1="10" X2="260" Y1="180" Y2="180" Stroke="Black" StrokeThickness="8" StrokeEndLineCap="Triangle"></Line>
- <Line X1="10" X2="260" Y1="200" Y2="200" StrokeEndLineCap="Round" StrokeThickness="10">
- <Line.Stroke>
- <LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
- <GradientStop Color="Blue"></GradientStop>
- <GradientStop Offset="1" Color="Red"></GradientStop>
- </LinearGradientBrush>
- </Line.Stroke>
- </Line>
- </Grid>
- </Window>
有一點需要特別註意,初學者認為繪圖一定要在Canvas中完成(誰叫它的名字叫畫布呢),其實不然,繪圖可以在任何一種佈局控制項中完成,WPF會自動根據容器的不同計算圖形的坐標,日常生活中,常用的繪圖容器有Canvas和Grid。
2 矩形
矩形有筆觸(Stroke,即邊線)和填充(Fill)構成。Stroke屬性的設置和Line一樣,Fill屬性的數據類型是Brush。Brush是一個抽象類,所以我們不可能拿一個Brush類的實例為Fill屬性賦值而只能用Brush派生類來進行賦值。WPF繪圖系統中包含非常豐富的Brush類型,常用的有:
SolidColorBrush:實心畫刷。在XAML中可以使用顏色名稱字元串直接賦值。
LinearGradientBrush:線性漸變畫刷。色彩沿設定的直線方向,按設定的變化點進行漸變。
RadialGradientBrush:徑向漸變畫刷。色彩沿半徑的方向、按設定的變化點進行漸變,形成圓形填充。
ImageBrsh:使用圖片作為填充類容。
DrawingBrush:使用矢量圖(Vector)和點陣圖(BitMap)作為填充內容。
VisualBrush:WPF中的每個控制項都是有FrameWrokElement派生而來的,而FrameWorkElment類又是由Visual類派生而來的。Visual意為“可視”之意,每個控制項的可視化形象就可以通過Visual類的方法獲得。獲得這個可視化形象之後,我們可以用這個形象進行填充,這就是VisualBrush。比如我想把窗體上的某個控制項拖到另外一個位置,當滑鼠鬆開之前需要在滑鼠指針下顯示一個幻影,這個幻影就是使用VisualBrush填充出來的一個矩形,並讓矩形捕捉滑鼠的位置、隨滑鼠移動。
下麵是使用不同畫刷填充矩形的綜合實例:
- <Window x:Class="WpfApplication1.Window46"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window46" Height="390" Width="600">
- <Grid>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="180" />
- <ColumnDefinition Width="10" />
- <ColumnDefinition Width="180" />
- <ColumnDefinition Width="10" />
- <ColumnDefinition Width="180*" />
- </Grid.ColumnDefinitions>
- <Grid.RowDefinitions>
- <RowDefinition Height="160" />
- <RowDefinition Height="10" />
- <RowDefinition Height="160" />
- </Grid.RowDefinitions>
- <!--實心填充-->
- <Rectangle Grid.Row="0" Grid.Column="0" Stroke="Black" Fill="LightBlue"></Rectangle>
- <!--線性漸變-->
- <Rectangle Grid.Row="0" Grid.Column="2">
- <Rectangle.Fill>
- <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
- <GradientStop Color="#FFB6F8F1" Offset="0"></GradientStop>
- <GradientStop Color="#FF0082BD" Offset="0.25"></GradientStop>
- <GradientStop Color="#FF95DEFF" Offset="0.6"></GradientStop>
- <GradientStop Color="#FF004F72" Offset="1"></GradientStop>
- </LinearGradientBrush>
- </Rectangle.Fill>
- </Rectangle>
- <!--徑向漸變-->
- <Rectangle Grid.Row="0" Grid.Column="4">
- <Rectangle.Fill>
- <RadialGradientBrush>
- <GradientStop Color="#FFB6F8F1" Offset="0"></GradientStop>
- <GradientStop Color="#FF0082BD" Offset="0.25"></GradientStop>
- <GradientStop Color="#FF95DEFF" Offset="0.75"></GradientStop>
- <GradientStop Color="#FF004F72" Offset="1.5"></GradientStop>
- </RadialGradientBrush>
- </Rectangle.Fill>
- </Rectangle>
- <!--圖片填充-->
- <Rectangle Grid.Row="2" Grid.Column="0">
- <Rectangle.Fill>
- <ImageBrush ImageSource="./01077_1.png" Viewport="0,0,0.3,0.3" TileMode="Tile">
- </ImageBrush>
- </Rectangle.Fill>
- </Rectangle>
- <Rectangle Grid.Row="2" Grid.Column="2">
- <Rectangle.Fill>
- <DrawingBrush Viewport="0,0,0.2,0.2" TileMode="Tile">
- <DrawingBrush.Drawing>
- <GeometryDrawing Brush="LightBlue">
- <GeometryDrawing.Geometry>
- <EllipseGeometry RadiusX="10" RadiusY="10"></EllipseGeometry>
- </GeometryDrawing.Geometry>
- </GeometryDrawing>
- </DrawingBrush.Drawing>
- </DrawingBrush>
- </Rectangle.Fill>
- </Rectangle>
- <!--無填充,使用線性漸變填充邊框-->
- <Rectangle Grid.Row="2" Grid.Column="5" StrokeThickness="10">
- <Rectangle.Stroke>
- <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
- <GradientStop Color="White" Offset="0.3"></GradientStop>
- <GradientStop Color="Blue" Offset="1"></GradientStop>
- </LinearGradientBrush>
- </Rectangle.Stroke>
- </Rectangle>
- </Grid>
- </Window>
運行效果如下圖:使用畫刷的時候,建議先在Blend裡面繪製圖大致的效果然後再在Visual Studio裡面微調。
接下來讓我們看一個VisualBrush的例子。為了簡單起見,目標控制項是一個Button,實際工作中換成複雜的控制項也一樣。程式的XAML代碼如下:
- <Window x:Class="WpfApplication1.Window47"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window47" Height="300" Width="400" Background="Orange">
- <Grid Margin="10">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="160" />
- <ColumnDefinition Width="*" />
- <ColumnDefinition Width="160" />
- </Grid.ColumnDefinitions>
- <StackPanel Background="White" x:Name="spleft">
- <Button Height="40" Content="OK" x:Name="btnReal" Click="btnReal_Click"></Button>
- </StackPanel>
- <Button Grid.Column="1" Content=">>" Margin="5,0"></Button>
- <StackPanel Grid.Column="2" Background="White" x:Name="spRight">
- </StackPanel>
- </Grid>
- </Window>
Button的事件處理器代碼如下:- double o = 1;//不透明度指數
- private void btnReal_Click(object sender, RoutedEventArgs e)
- {
- VisualBrush vb = new VisualBrush(this.btnReal);
- Rectangle rtg = new Rectangle();
- rtg.Width = btnReal.Width;
- rtg.Height = btnReal.Height;
- rtg.Fill = vb;
- rtg.Opacity = o;
- o -= 0.2;
- this.spRight.Children.Add(rtg);
- }
運行效果如下圖:3. 橢圓
橢圓也是一種常見的幾何圖形,它的使用方法和矩形沒有什麼區別。下麵的例子是繪製一個球體,球體的輪廓是正圓(Circle),Width和Height相等的橢圓即為正圓:球體的光影使用徑向漸變實現,XAML代碼如下:
- <Window x:Class="WpfApplication1.Window48"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window48" Height="300" Width="300">
- <Grid>
- <Ellipse Height="140" Name="ellipse1" Stroke="Gray" Width="140" Cursor="Hand" ToolTip="A Ball">
- <Ellipse.Fill>
- <RadialGradientBrush GradientOrigin="0.2,0.8" RadiusX="0.75" RadiusY="0.75">
- <RadialGradientBrush.RelativeTransform>
- <TransformGroup>
- <RotateTransform Angle="90" CenterX="0.5" CenterY="0.5"></RotateTransform>
- </TransformGroup>
- </RadialGradientBrush.RelativeTransform>
- <GradientStop Color="#FFFFFFFF" Offset="0" />
- <GradientStop Color="#FF444444" Offset="0.66" />
- <GradientStop Color="#FF999999" Offset="1" />
- </RadialGradientBrush>
- </Ellipse.Fill>
- </Ellipse>
- </Grid>
- </Window>
與前面提到的一樣,橢圓的繪製和色彩填充在Blend裡面完成的,在VS裡面又做了一些相應的調整。
4 路徑
路徑(Path)可以說是WPF繪圖最強大的工具,一來是因為它完全可以替代其它幾種圖形,而來它可以將直線,圓弧,貝塞爾曲線等基本元素組合起來,形成更複雜的圖形。路徑最重要的一個屬性就是Data,Data的數據類型是Geometry(幾何圖形),我們正是使用這個屬性將一些基本的線段拼接起來,形成複雜的圖形。
為Data屬性賦值的方法有兩種:一種是標簽式的標準語法,另外一種是專門用於繪製幾何圖形的“路徑標記語法”。本小節我們使用標準標簽語法認識各種線段,下一節我們將學習繪製幾何圖形的路徑標記語法。
想要使用Path路徑繪製圖形,首先要知道幾何圖形數據是如何組合到Data屬性中的。Path的Data屬性是Geometry類,但是Geometry類是一個抽象類,所以我們不可能在XAML中直接使用<Geometry>標簽。
- <!--不可能出現-->
- <Path>
- <Geometry>
- <!---->
- </Geometry>
- </Path>
我們可以使用Geometry的子類。Geometry的子類包括:LineGeometry:直線幾何圖形。
RectangleGeometry:矩形幾何圖形。
EllipseGeometry:橢圓幾何圖形。
PathGeometry:路徑幾何圖形。
StreamGeometry:PathGeometry的輕量級替代品,不支持Binding、動畫等效果。
CombinedGeometry:由多個基本幾何圖形關聯在一起,形成的單一幾何圖形。
GeometryGroup:由多個基本幾何圖形組合在一起,形成的幾何圖形組。
可能讓大家比較迷惑的是:前面已經見過Line,Rectangle,Ellipse等類,怎麼現在又出來了LineGeometry、RectangleGeometry、EllipseGeometry類呢?它們的區別在於前面介紹的Line,Rectangle,Ellipse都是可以獨立存在的對象,而這些*Geometry類只能用於結合成其它幾何圖形、不能獨立存在-----當我們在Blend裡面選中一組獨立的幾何圖形併在菜單里執行組合路徑命令時,本質上就是把原來獨立的Line,Rectangle,Ellipse對象轉換成了*Geometry對象並結合成一個新的複雜幾何圖形。
回到Data的Path屬性,下麵這個例子簡要的展示了各種幾何圖形:
- <Window x:Class="WpfApplication1.Window49"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window49" Height="350" Width="340">
- <Grid>
- <Grid.RowDefinitions>
- <RowDefinition Height="160" />
- <RowDefinition Height="160" />
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="160" />
- <ColumnDefinition Width="160" />
- </Grid.ColumnDefinitions>
- <!--直線-->
- <Path Stroke="Blue" StrokeThickness="2" Grid.Row="0" Grid.Column="0">
- <Path.Data> <