Canvas面板允許使用精確的坐標放置元素,如果設置數據驅動的富窗體和標准對話框,這並非好的選擇;但如果需要構建其他一些不同的內容(例如,為圖形工具創建創建繪圖表面),Canvas面板可能是個有用的工具。Canvas面板還是最輕量級的布局容器。這是因為Canvas面板沒有包含任何復雜的布局邏輯,用以改變其子元素的首選尺寸。Canvas面板只是在指定的位置放置其子元素,並且子元素具有所希望的精確尺寸。
為在Canvas面板中定位元素,需要設置Canvas.Left和Canvas.Top附加屬性。Canvas.Left屬性設置元素左邊和Canvas面板左邊之間的單位數。Canvas.Top屬性設置子元素頂部和Canvas面板頂邊之間的單位數。同樣,這些數值也是以設備無關單位設置的。當將系統DPI設置為96dpi時,設備無關單位恰好等於通常的像素。
可使用Width和Height屬性明確設置子元素的尺寸。與使用其他面板相比,使用Canvas面板時這種設置更普遍,因為Canvas面板沒有自己的布局邏輯(並且當需要精確控制組合元素如何排列時,經常會使用Canvas面板)。如果沒有設置Width和Height屬性,元素會獲取它所期望的尺寸——換句話說,它將變得足夠大以適應其內容。
下面是一個使用Canvas的示例:
<Window x:Class="CanvasLayout.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas> <Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button> <Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button> <Button Canvas.Left="60" Canvas.Top="80" Width="80" Height="60">(60,80)</Button> <Button Canvas.Left="180" Canvas.Top="150" Width="100" Height="60">(180,150)</Button> </Canvas> </Window>
最終效果如下圖所示:
如果改變窗口的大小,Canvas面板就會拉伸以填滿可用空間,但Canvas面板上的控件不會改變其尺寸和位置。Canvas面板不包含任何錨定和停靠功能,這兩個功能是在Windows窗體中使用坐標布局提供的。造成該問題的部分原因是為了保持Canvas面板的輕量級,另一個原因是為了防止以不當目的使用Canvas面板(例如,確定標准用戶界面的布局)。
與其他所有布局容器一樣,可在用戶界面中嵌套Canvas面板。這意味着可使用Canvas面板在窗口的一部分繪制一些細節內容,而在窗口的其余部分使用更合乎標准的WPF面板。
一、Z順序
如果Canvas面板中有多個互相重疊的元素,可通過設置Canvas.ZIndex附加屬性來控制他們的層疊方式。
添加的所有元素通常都具有相同的ZIndex指——0。如果元素具有相同的ZIndex值,就按他們在Canvas.Children集合中的順序進行顯示,這個順序依賴於元素在XAML標記中定義的順序。在標記靠后位置聲明的元素會顯示在前面聲明的元素的上面。
然而,可通過增加任何子元素的ZIndex值來提高層次級別。因為具有更高ZIndex值的元素始終顯示在較低ZIndex值得元素的上面。如下代碼所示:
<Window x:Class="CanvasLayout.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas> <Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button> <Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button> <Button Canvas.Left="180" Canvas.Top="110" Canvas.ZIndex="1" Width="80" Height="60">(180,110)</Button> <Button Canvas.Left="180" Canvas.Top="150" Width="100" Height="60">(180,150)</Button> </Canvas> </Window>
效果如下圖所示:
如果需要通過代碼來改變元素的位置,ZIndex屬性是非常有用的。只需要調用Canvas.SetZIndex()方法,並傳遞希望修改的元素和希望使用的新ZIndex值即可。遺憾的是,並不存在BringToFront()或SendToBack()方法——要實現這一行為,需要跟蹤最高和最低的ZIndex值。
二、InkCanvas元素
WPF還提供了InkCanvas元素,它與Canvas面板在某些方法是類似的(而在其他方面卻完全不同)。和Canvas面板一樣,InkCanvas元素定義了4個附加屬性(Top、Left、Bottom和Right),可將這4個附加屬性應用於子元素,以根據坐標進行定位。然而,基本的內容區別很大——實際上,InkCanvas類不是派生自Canvas類,甚至也不是派生自Panel基類,而是直接派生自FrameworkElement類。
InkCanvas元素的主要目的用於接收手寫筆輸入。手寫筆是一種在平板PC中使用的類似鋼筆的輸入設備,然而,InkCanvas元素同事也可使用鼠標進行工作,就像使用手寫筆一樣。因此,用戶可使用鼠標在InkCanvas元素上繪制線條,或者選擇以及操作InkCanvas中的元素。
InkCanvas元素實際上包含兩個子內容集合。一個是為人熟知的Children集合,它保存任意元素,就像Canvas面板一樣。每個子元素可根據Top、Left、Bottom和Right屬性進行定位。另一個是Strokes結合,它保存System.Windows.Ink.Stroke對象,該對象表示用戶在InkCanvas元素上繪制的圖形輸入。用戶繪制的每條直線或曲線都變成獨立的Stroke對象。得益於這兩個集合,可使用InkCanvas讓用戶使用存儲在Strokes集合中的筆畫(Stroke)為保存在Children集合中的內容添加注釋。
下面的這個示例中包含一副圖片的InkCanvas元素,這幅圖片已經使用附加的筆畫注釋過。
<Window x:Class="CanvasLayout.InkCanvasWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="InkCanvasWindow" Height="300" Width="385.714"> <InkCanvas Name="inkCanvas" Background="LightYellow" EditingMode="Ink"> <Image Source="背景.jpg" Width="300" Height="240" Stretch="Fill" InkCanvas.Top="10" InkCanvas.Left="10"></Image> </InkCanvas> </Window>
效果圖如下所示:
筆畫是在用戶在運行時繪制的。
根據為InkCanvas.EditingMode屬性設置的值,可以采用截然不同的方式使用InkCanvas元素,下表列出了所有選項:
InkCanvas元素會引發多種事件,當編輯模式改變時會引發ActiveEditingModeChanged事件,在GestureOnly或InkAndGesture模式下刪除姿勢時會引發Gesture事件,在Select模式下選擇元素或改變元素時會引發SelectionChanging事件、SelectionChanged事件、SelectionMoving事件、SelectionMoved事件、SelectionResizing事件和SelectionResized事件。其中,名稱以"ing"結尾的事件表示動作將要發生,但可以通過設置EventArgs對象的Cancel屬性取消事件。
后期還會進行更詳細的介紹。