【WPF】 Canvas介紹
從這篇文章開始是對WPF中的介面如何佈局做一個較簡單的介紹,大家都知道:UI是做好一個軟體很重要的因素,如果沒有一個漂亮的UI,功能做的再好也無法吸引很多使用者使用,而且沒有漂亮的介面,那麼普通使用者會感覺這個軟體沒有多少使用價值。
1、canvas 中的元素通過canvas的附件屬性left|right、bottom|top 來佈局, 不能同時使用left 和right或者top和bottom
2、zindex控制層級。
一. 總體介紹
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”來裁剪多出的內容。
如果將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屬性,那麼後者將會被忽略。
2、ZIndex屬性
Canvas面板中可能會有多個相互重疊的元素,可以設定Canvas的ZIndex附加屬性,來控制他們的重疊方式 ZIndex屬性預設值都是0,屬性值必須是整數。
除此之外,我們還可以通過程式碼的方式設定ZIndex的值,程式碼如下:Canvas.SetZIndex(this.btn, 2);
3、測量處理過程(Measure)和排列處理過程(Arrange)
從這篇文章開始是對WPF中的介面如何佈局做一個較簡單的介紹,大家都知道:UI是做好一個軟體很重要的因素,如果沒有一個漂亮的UI,功能做的再好也無法吸引很多使用者使用,而且沒有漂亮的介面,那麼普通使用者會感覺這個軟體沒有多少使用價值。
1、canvas 中的元素通過canvas的附件屬性left|right、bottom|top 來佈局, 不能同時使用left 和right或者top和bottom
2、zindex控制層級。
一. 總體介紹
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”來裁剪多出的內容。
如果將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屬性,那麼後者將會被忽略。
2、ZIndex屬性
Canvas面板中可能會有多個相互重疊的元素,可以設定Canvas的ZIndex附加屬性,來控制他們的重疊方式 ZIndex屬性預設值都是0,屬性值必須是整數。
除此之外,我們還可以通過程式碼的方式設定ZIndex的值,程式碼如下:Canvas.SetZIndex(this.btn, 2);