不少時候在編寫程序界面的時候都會忽略了應用佈局的重要性,僅僅只是把佈局看做是對UI元素的排列,只要能實現佈局的效果就能夠了,可是在實際的產品開發中這是遠遠不夠的,你可能面臨要實現的佈局效果要比常規佈局更加複雜,這就須要對佈局的技術知識有深刻的理解和掌握纔可以實現。要實現一個佈局的效果,可能會有不少總佈局方案,咱們該怎麼去選擇實現的方法?若是要實現的一個佈局效果是比較複雜的,咱們該怎麼去對這種佈局規律進行封裝?要解決這些問題,首要的問題就是須要咱們對程序的佈局原理有着深刻的理解。html
佈局是頁面編程的第一步,是從整體的方向去把握頁面上UI元素的顯示。佈局其實一個一個應用程序開發裏面很是重要的一部分,這塊的知識也是每每會被開發者所忽視。隨着Windows 10支持這很是多的設備和屏幕分辨率,佈局顯得原來越重要,也將會變得更加複雜,咱們很是有必要去了解作程序的界面佈局有什麼樣的意義,這不只僅是界面顯示能夠就完事了。下面來看一下應用程序的佈局有什麼樣的意義:編程
(1)代碼邏輯微信
良好的佈局會使得代碼邏輯很是清晰,差的佈局方案會讓頁面代碼邏輯很混亂。若是你只是靠拖拉控件來作Windows 10的佈局,這個程序的界面佈局確定會變成很糟糕,因此好的佈局方案,必定要基於對各類佈局控件的理解,而後充分地它們的特性去實現佈局的效果。框架
(2)效率性能ide
佈局不只僅是界面UI的事情,它甚至會影響到程序的運行效率。固然簡單的幾個控件的頁面佈局,對程序效率性能的影響是微乎其微的,可是若是你的界面要展現大量的控件的時候,這時候佈局的好壞就會直接影響到程序的效率。好的佈局實現邏輯會讓程序即便在有大量控件的頁面也能流程的運行。佈局
(3)動態適配性能
動態適配包括兩個方面,一個是Windows 10的多種分辨率的界面的適配,另一個是頁面的控件是不肯定的也會產生動態適配的問題。好的佈局方案,可使應用程序能夠兼容各類分辨率的設別,受到不一樣分辨率所影響的頁面,也是能夠經過佈局的技巧來解決的,保證不一樣的分辨率下面都是符合產品的顯示效果。還有一個就是動態產生的控件,這個是指你的頁面上會根據不一樣的狀況顯示不一樣的內容,這時候在作佈局的時候就要思考如何對付這些會變化的頁面。測試
(4)實現複雜的佈局this
有時候頁面須要實現一些複雜的佈局效果,好比像圓圈必定排列控件,Windows 10裏面是沒有這樣的佈局控件支持這種複雜的佈局效果的,這時候就須要去自定義佈局的規律來解決這樣的問題。可否自定義佈局控件去實現複雜的佈局效果,這就要看你對Windows 10的佈局技術的掌握程度了。spa
佈局系統是指對Windows 10的佈局面板所進行的佈局過程的運做原理的統稱。佈局實際上是一個在 Windows 10應用中調整對象大小和定位對象的過程。要定位可視化對象,必須將它們放置於 Panel 或其餘佈局面板中。Panel類是全部佈局面板的父類,系統的佈局面板Canvas、StackPanel、Grid和RelativePanel都是Panel類的子類,繼承了全部Panel類的特性。Panel類定義了在屏幕上繪製全部的面板裏面的成員(Children屬性)的佈局行爲。這是一個計算密集型過程,即 Children 集合越大,執行的計算次數就越多,也就是面板裏面的元素越多,佈局系統佈局的整個過程的時間就會越長。全部的佈局面板類都是在Panel的基礎上添加了相應的佈局規律的,在Panel類的基礎上繼續封裝的佈局面板是爲了更好地解決一些佈局規律的問題,實際上這樣的封裝是增長複雜性,對性能形成必定的損失的。因此若是不須要較爲複雜的佈局面板(如 Grid),則可使用構造相對簡單的佈局(如 Canvas),這種佈局可產生更佳的性能。
簡單地說,佈局是一個遞歸系統,實如今屏幕上對元素進行大小調整、定位和繪製。在整個佈局的過程當中,佈局系統對佈局面板成員的處理分爲兩個過程:第一個是測量處理過程,第二個是排列處理過程。測量處理過程是肯定每一個子元素所需大小的過程。排列處理過程是最終肯定每一個子元素的大小和位置的過程。每當面板裏面的成員改變其位置時,佈局系統就可能觸發一個新的處理過程,從新處理上面所說的兩個過程。不論什麼時候調用佈局系統,都會發生如下一系列的操做:
一、第一次遞歸遍歷測量每一個佈局面板子元素(UIElement類的子類控件)的大小。
二、計算在 FrameworkElement類的子類控件元素上定義的大小調整屬性,例如 Width、Height 和 Margin。
三、應用佈局面板特定的邏輯,例如StackPanel面板的水平佈局。
四、第二次遞歸遍歷負責把子元素排到對於本身的相對的位置。
五、把全部的子元素繪製到屏幕上。
若是其餘子元素添加到了集合中、子元素的佈局屬性(如 Width 和 Height)發生了改變或調用了 UpdateLayout 方法,均會再次調用該過程。所以,瞭解佈局系統的特性就很重要,由於沒必要要的調用可能致使應用性能變差。下面的內容將會詳細地演示這個佈局的過程。
在Windows 10中,佈局不只僅是佈局面板的要作的事情,佈局面板是在負責把這個佈局的過程組織起來,而在整個佈局的過程當中對於佈局面板裏面的元素都要通過一個從最外面到最裏面的一個遞歸的測量和排列的過程。在研究這個遞歸的排列和測試的過程,先來了解一下,基本的控件上關於佈局的一些重要的方法和屬性。
Windows 10的UI元素有兩個很是重要的基類UIElement類和FrameworkElement類,他們的繼承層次結構以下:
Windows.UI.Xaml.DependencyObject
Windows.UI.Xaml.UIElement
Windows.UI.Xaml.FrameworkElement
(1)UIElement類
UIElement類是具備可視外觀並能夠處理基本輸入的大多數對象的基類。關於佈局,UIElement類有兩個很是重要的屬性——DesiredSize和RenderSize屬性和兩個很是重要的方法——Measure 方法和Arrange方法。
DesiredSize屬性:這是一個只讀的屬性,類型是Size類,表示在佈局過程的測量處理過程當中計算的大小。
RenderSize屬性:這是一個只讀的屬性,類型是Size類,表示UI元素最終呈現大小,RenderSize和DesiredSize並不必定是相等的。RenderSize就是其ArrangeOverride方法的返回值。
public void Measure(Size availableSize)方法:Measure方法所作的事情是更新 UIElement 的 DesiredSize屬性,測量出UI元素的大小。若是在該UI元素上實現了FrameworkElement.MeasureOverride(System.Windows.Size)方法,將會用此方法以造成遞歸佈局更新。參數availableSize表示:父對象能夠爲子對象分配的可用空間。子對象能夠請求大於可用空間的空間,若是該特定面板中容許滾動或其餘調整大小行爲,則提供的大小能夠適應此空間。
public void Arrange(Rect finalRect)方法:Arrange方法所作的事情是定位子對象並肯定 UIElement 的大小,也就是DesiredSize屬性的值。若是在該UI元素上實現了FrameworkElement. ArrangeOverride(System.Windows.Size)方法,將會用此方法以造成遞歸佈局更新。參數finalRect表示:佈局中父對象爲子對象計算的最終大小,做爲 System.Windows.Rect 值提供。
(2)FrameworkElement類
FrameworkElement類是UIElement類的子類,爲 Windows 10佈局中涉及的對象提供公共 API 的框架。FrameworkElement類有兩個和佈局相關的虛方法MeasureOverride 方法和ArrangeOverride方法。若是已經存在的佈局面板沒法知足特殊的佈局需求,你可能須要自定義佈局面板,就須要重寫MeasureOverride和ArrangeOverride兩個方法,而這兩個方法是Windows 10的佈局系統提供給用戶的自定義接口,下面來看下這兩個方法的含義。
protected virtual Size MeasureOverride(Size availableSize):提供 Windows 10佈局的度量處理過程的行爲,能夠重寫該方法來定義其本身的度量處理過程行爲。參數availableSize表示對象能夠賦給子對象的可用大小,能夠指定無窮大值 (System.Double.PositiveInfinity),以指示對象的大小將調整爲可用內容的大小,若是子對象所計算出來的大小比availableSize大,那麼將會被截取出availableSize大小的部分。返回結果表示此對象在佈局過程當中基於其對子對象分配大小的計算或者基於固定面板大小等其餘因素而肯定的它所需的大小。
protected virtual Size ArrangeOverride(Size finalSize):提供 Windows 10佈局的排列處理過程的行爲,能夠重寫該方法來定義其本身的排列處理過程行爲。參數finalSize表示父級中此對象應用來排列自身及其子元素的最終區域。返回結果表示元素在佈局中排列後使用的實際大小。
Windows 10的佈局系統是一個遞歸系統,它老是以Measure方法開始,最後以Ararnge方法結束。假設在整個佈局系統裏面只有一個對象,這個對象在加載到界面以前會先調用Measure方法來測量對象的大小,最後再調用Ararnge方法來安排對象的位置完成了整個過程的佈局。可是現實中每每是一個對象裏面包含了多個子對象,子對象裏面也包含着子對象,如此遞歸下去,直到最底下的對象。佈局的過程就是從最頂層的對象開始測量,最頂層的對象的測量過程又會調用它的子對象的測量方法,如此遞歸直到最底下的對象。測量的過程完成以後,則開始排列的過程,排列的過程也是和測量的過程的原理同樣,一直遞歸下去直到最底下的對象。下面經過一個示例來模擬這個過程。
示例裏面建立了兩個類,TestPanel類用來模擬最外面的佈局面板,做爲父對象的角色;TestUIElement類用來模擬做爲佈局面板的元素,做爲最底下子對象的角色。TestUIElement類和TestPanel類都繼承了Panel類,實現了MeasureOverride和ArrangeOverride方法,並打印出相關的日誌用於跟蹤佈局的詳細狀況。
代碼清單5-8:模擬測量和排列的過程(源代碼:第5章\Examples_5_8)
TestUIElement.cs文件主要代碼 ------------------------------------------------------------------------------------------------------------------ public class TestUIElement : Panel { protected override Size MeasureOverride(Size availableSize) { Debug.WriteLine("進入子對象" + this.Name + "的MeasureOverride方法測量大小"); return availableSize; } protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) { Debug.WriteLine("進入子對象" + this.Name + "的ArrangeOverride方法進行排列"); return finalSize; } }
TestPanel.cs文件主要代碼 ----------------------------------------------------------------------------------------------------------------- public class TestPanel : Panel { protected override Size MeasureOverride(Size availableSize) { Debug.WriteLine("進入父對象" + this.Name + "的MeasureOverride方法測量大小"); foreach (UIElement item in this.Children) { item.Measure(new Size(120, 120));//這裏是入口 Debug.WriteLine("子對象的DesiredSize值 Width:" + item.DesiredSize.Width + " Height:" + item.DesiredSize.Height); Debug.WriteLine("子對象的RenderSize值 Width:" + item.RenderSize.Width + " Height:" + item.RenderSize.Height); } Debug.WriteLine("父對象的DesiredSize值 Width:" + this.DesiredSize.Width + " Height:" + this.DesiredSize.Height); Debug.WriteLine("父對象的RenderSize值 Width:" + this.RenderSize.Width + " Height:" + this.RenderSize.Height); return availableSize; } protected override Size ArrangeOverride(Size finalSize) { Debug.WriteLine("進入父對象" + this.Name + "的ArrangeOverride方法進行排列"); double x = 0; foreach (UIElement item in this.Children) { //排列子對象 item.Arrange(new Rect(x, 0, item.DesiredSize.Width, item.DesiredSize.Height)); x += item.DesiredSize.Width; Debug.WriteLine("子對象的DesiredSize值 Width:" + item.DesiredSize.Width + " Height:" + item.DesiredSize.Height); Debug.WriteLine("子對象的RenderSize值 Width:" + item.RenderSize.Width + " Height:" + item.RenderSize.Height); } Debug.WriteLine("父對象的DesiredSize值 Width:" + this.DesiredSize.Width + " Height:" + this.DesiredSize.Height); Debug.WriteLine("父對象的RenderSize值 Width:" + this.RenderSize.Width + " Height:" + this.RenderSize.Height); return finalSize; } }
建立了TestUIElement類和TestPanel類以後,接下來要在UI上使用這兩個類,把TestPanel看做是佈局面板控件來使用,把TestPanel看做是普通控件來使用,而後觀察打印出來的運行日誌。要添加這兩個控件須要如今xaml頁面上把這兩個空間所在的空間引入進去再進行調用,以下面的代碼所示:
MainPage.xaml文件主要代碼 ----------------------------------------------------------------------------------------------------------------- ……省略若干代碼 下面引入控件空間 xmlns:local="using:MeasureArrangeDemo" ……省略若干代碼 下面調用控件佈局 <StackPanel> <Button Content="改變高度" Click="Button_Click_1"></Button> <local:TestPanel x:Name="panel" Height="400" Width="400" Background="White" > <local:TestUIElement x:Name="element1" Width="60" Height="60" Background="Red" Margin="10"/> <local:TestUIElement x:Name="element2" Width="60" Height="60" Background="Red" /> </local:TestPanel> </StackPanel>
程序在Debug的狀態下運行以後,在Visual Studio的輸出窗口能夠看到打印出來的日誌。日誌的詳細狀況以下:
/*日誌開始*/
進入父對象panel的MeasureOverride方法測量大小
進入子對象element1的MeasureOverride方法測量大小
子對象的DesiredSize值 Width:80 Height:80
子對象的RenderSize值 Width:0 Height:0
進入子對象element2的MeasureOverride方法測量大小
子對象的DesiredSize值 Width:60 Height:60
子對象的RenderSize值 Width:0 Height:0
父對象的DesiredSize值 Width:0 Height:0
父對象的RenderSize值 Width:0 Height:0
進入父對象panel的ArrangeOverride方法進行排列
進入子對象element1的ArrangeOverride方法進行排列
子對象的DesiredSize值 Width:80 Height:80
子對象的RenderSize值 Width:60 Height:60
進入子對象element2的ArrangeOverride方法進行排列
子對象的DesiredSize值 Width:60 Height:60
子對象的RenderSize值 Width:60 Height:60
父對象的DesiredSize值 Width:400 Height:400
父對象的RenderSize值 Width:0 Height:0
/*日誌結束*/
從打印出來的日誌能夠很清楚地看到整個佈局過程的步驟(如圖5.25所示),以及佈局過程當中DesiredSize值和RenderSize值的變化狀況。
從佈局的過程當中能夠總結出下面的結論:
1. 測量的過程是爲了確認DesiredSize的值,最終是要提供給排列的過程去使用。
2. DesiredSize是根據Margin,,Width,Height等屬性來決定。
3. 排列的過程肯定RenderSize,以及最終子對象被安置的空間。RenderSize就是ArrangeOverride的返回值,沒還有被裁剪過的值。
4. Margin,,Width,Height等屬性只是控件表面上的屬性,而實際掌控住這些效果的是佈局的測量排列過程。
5. Margin,,Width,Height等屬性的改變會從新觸發佈局的過程。
源代碼下載:http://vdisk.weibo.com/u/2186322691
目錄:http://www.cnblogs.com/linzheng/p/5021428.html
歡迎關注個人微博@WP林政 微信公衆號:wp開發(號:wpkaifa)
Windows10/WP技術交流羣:284783431