從這篇文章開始是對WPF中的界面如何布局做一個較簡單的介紹,大家都知道:UI是做好一個軟件很重要的因素,如果沒有一個漂亮的UI,功能做的再好也無法吸引很多用戶使用,而且沒有漂亮的界面,那么普通用戶會感覺這個軟件沒有多少使用價值。
一. 總體介紹
WPF的布局控件都在System.Windows.Controls.Panel這個基類下面,使用 WPF提供的各種控件在WPF應用程序中界面進行布局,同時對各種子控件(如按鈕、文本框,下拉框等)進行排列組合。
Pane類的公共屬性太多了。就簡單介紹幾個常見的屬性如下表。
|
名稱 |
說明 |
|
Cursor |
獲取或設置在鼠標指針位于此元素上時顯示的光標。 |
|
DataContext |
獲取或設置元素參與數據綁定時的數據上下文。 |
|
Dispatcher |
獲取與此 DispatcherObject 關聯的 Dispatcher。 |
|
FontFamily |
獲取或設置控件的字體系列。 |
|
FontSize |
獲取或設置字號。 |
|
FontWeight |
獲取或設置指定的字體的權重或粗細。 |
|
Foreground |
獲取或設置描述前景色的畫筆。 |
|
HandlesScrolling |
獲取一個值控件是否支持滾動。 |
|
Height |
獲取或設置元素的建議高度。 |
|
HorizontalContentAlignment |
獲取或設置控件內容的水平對齊。 |
|
IsLoaded |
獲取一個值,該值指示是否已加載此元素以供呈現。 |
|
IsMouseOver |
獲取一個值,該值指示鼠標指針是否位于此元素(包括可視樹上的子元素)上。這是一個依賴項屬性。 |
|
IsTabStop |
獲取或設置一個值控制是否在選項卡上導航包含。 |
|
IsVisible |
獲取一個值,該值指示此元素在用戶界面 (UI) 中是否可見。這是一個依賴項屬性。 |
|
LayoutTransform |
獲取或設置在執行布局時應該應用于此元素的圖形轉換方式。 |
|
Margin |
獲取或設置元素的外邊距。 |
|
Name |
獲取或設置元素的標識名稱。 該名稱提供一個引用,以便當 XAML 處理器在處理過程中構造標記元素之后,代碼隱藏(如事件處理程序代碼)可以對該元素進行引用。 |
|
Opacity |
獲取或設置當 UIElement 在用戶界面 (UI) 中呈現時為其整體應用的不透明度因子。這是一個依賴項屬性。 |
|
Padding |
獲取或設置控件中的空白。 |
|
RenderTransform |
獲取或設置影響此元素的呈現位置的轉換信息。這是一個依賴項屬性。 |
|
TabIndex |
獲取或設置使用 tab 鍵時,確定順序接收焦點的元素的值,當用戶將控件定位。 |
|
Tag |
獲取或設置任意對象值,該值可用于存儲關于此元素的自定義信息。 |
|
ToolTip |
獲取或設置在用戶界面 (UI) 中為此元素顯示的工具提示對象。 |
|
TouchesCaptured |
獲取在此元素上捕獲的所有觸摸設備。 |
|
TouchesCapturedWithin |
獲取在此元素或其可視化樹中的任何子元素上捕獲的所有觸摸設備。 |
|
VerticalContentAlignment |
獲取或設置控件內容的垂直對齊方式。 |
|
Visibility |
獲取或設置此元素的用戶界面 (UI) 可見性。這是一個依賴項屬性。 |
|
VisualOpacityMask |
獲取或設置 Brush 值,該值表示 Visual 的不透明蒙板。 |
|
Width |
獲取或設置元素的寬度。 |
一個Panel 的呈現就是測量和排列子控件,然后在屏幕上繪制它們。所以在布局的過程中會經過一系列的計算,那么子控件越多,執行的計算次數就越多,則性能就會變差。如果不需要進行復雜的布局,則盡量少用復雜布局控件(如 Grid和自定義復雜的Panel);如果能簡單布局實現就盡量使用構造相對簡單的布局(如 Canvas、UniformGrid等),這種布局可帶來更好的性能。 如果有可能,我們應盡量避免調用 UpdateLayout方法。
每當Panel內的子控件改變其位置時,布局系統就可能觸發一個新的處理過程。對此,了解哪些事件會調用布局系統就很重要,因為不必要的調用可能導致應用程序性能變差。
換句話說,布局是一個遞歸系統,實現在屏幕上對控件進行大小調整、定位和繪制,然后進行呈現。具體如下圖,要實現控件0的布局,那么先要實現0的子控件 01,02...的布局,要實現01的布局,那么得實現01的子控件001,002...的布局,如此循環直到子控件的布局完成后,再完成父控件的布局, 最后遞歸回去直到遞歸結束,這樣整個布局過程就完成了.
布局系統為Panel中的每個子控件完成兩個處理過程:測量處理過程(Measure)和排列處理過程(Arrange)。每個子 Panel 均提供自己的 MeasureOverride 和 ArrangeOverride 方法,以實現自己特定的布局行為。
二. Canvas
Canvas是最基本的面板,只是一個存儲控件的容器,它不會自動調整內部元素的排列及大小,它僅支持用顯式坐標定位控件,它也允許指定相對任何角的坐標,而不僅僅是左上角。可以使用Left、Top、Right、 Bottom附加屬性在Canvas中定位控件。通過設置Left和Right屬性的值表示元素最靠近的那條邊,應該與Canvas左邊緣或右邊緣保持一個固定的距離,設置Top和Bottom的值也是類似的意思。實質上,你在選擇每個控件停靠的角時,附加屬性的值是作為外邊距使用的。如果一個控件沒有使 用任何附加屬性,它會被放在Canvas的左上方(等同于設置Left和Top為0)。
Canvas的主要用途是用來畫圖。Canvas默認不會自動裁減超過自身范圍的內容,即溢出的內容會顯示在Canvas外面,這是因為默認 ClipToBounds=”False”;我們可以通過設置ClipToBounds=”True”來裁剪多出的內容。
接下來我們來看兩個實例,第一個實例使用XAML代碼實現:
<Window x:Class="WpfApp1.WindowCanvas" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WindowCanvas" Height="400" Width="500"> <Grid> <Canvas Margin="0,0,0,0" Background="White"> <Rectangle Fill="Blue" Stroke="Azure" Width="250" Height="200" Canvas.Left="210" Canvas.Top="101"/> <Ellipse Fill="Red" Stroke="Green" Width="250" Height="100" Panel.ZIndex="1" Canvas.Left="65" Canvas.Top="45"/> </Canvas> <Canvas> <Button Name="btnByCode" Click="btnByCode_Click">后臺代碼實現</Button> </Canvas> </Grid> </Window>
實例后的效果如下圖。
第二個實例,我們使用后臺代碼來實現。下面紅色字體是如何設置控件的Canvas.Left 和Canvas.Top值
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfApp1 { /// <summary> /// WindowCanvas.xaml 的交互邏輯 /// </summary> public partial class WindowCanvas : Window { public WindowCanvas() { InitializeComponent(); } public void DisplayCanvas() { Canvas canv = new Canvas(); //把canv添加為窗體的子控件 this.Content = canv; canv.Margin = new Thickness(0, 0, 0, 0); canv.Background = new SolidColorBrush(Colors.White); //Rectangle Rectangle r = new Rectangle(); r.Fill = new SolidColorBrush(Colors.Red); r.Stroke = new SolidColorBrush(Colors.Red); r.Width = 200; r.Height = 140; r.SetValue(Canvas.LeftProperty, (double)200); r.SetValue(Canvas.TopProperty, (double)120); canv.Children.Add(r); //Ellipse Ellipse el = new Ellipse(); el.Fill = new SolidColorBrush(Colors.Blue); el.Stroke = new SolidColorBrush(Colors.Blue); el.Width = 240; el.Height = 80; el.SetValue(Canvas.ZIndexProperty, 1); el.SetValue(Canvas.LeftProperty, (double)100); el.SetValue(Canvas.TopProperty, (double)80); canv.Children.Add(el); } private void btnByCode_Click(object sender, RoutedEventArgs e) { DisplayCanvas(); } } }
實現后的效果如下圖。
最后 要說明一點Canvas內的子控件不能使用兩個以上的Canvas附加屬性,如果同時設置Canvas.Left和Canvas.Right屬性,那么后者將會被忽略。
文章列表
留言列表