控件基礎: Measure, Arrange, GeneralTransform, Visua
介紹
重新想象 Windows 8 Store Apps 之 控件基礎
Measure() 和 Arrange() - xaml 的 layout 系統
GeneralTransform - 通過 UIElement.TransformToVisual() 獲取元素的位置信息
VisualTree - 可視樹
示例
1、演示 xaml 的 layout 系統
Controls/Basic/MeasureArrange.xaml
<Page x:Class="XamlDemo.Controls.Basic.MeasureArrange" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.Basic" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <local:MyStackPanel Margin="120 0 0 0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" Height="200"> <TextBlock Text="我是文本" Width="100" Height="100" /> <Button Content="我是按鈕" Width="150" Height="150" /> </local:MyStackPanel> </Grid> </Page>
Controls/Basic/MeasureArrange.xaml.cs
/* * 演示 Layout 系統 * * win8 xaml 的 layout 就是一個遞歸系統,本 demo 就遞歸的一個過程做說明(步驟順序參見代碼注釋中 的序號) */ using System; using System.Diagnostics; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlDemo.Controls.Basic { public sealed partial class MeasureArrange : Page { public MeasureArrange() { this.InitializeComponent(); } } public class MyStackPanel : StackPanel { // 1、首先爸爸知道自己能夠提供的尺寸 availableSize,然後告訴兒子們 protected override Size MeasureOverride(Size availableSize) { // 2、兒子們收到 availableSize 後,又結合了自身的實際情況,然後告訴爸爸兒子們所期望的尺寸 desiredSize Size desiredSize = base.MeasureOverride(availableSize); Debug.WriteLine("availableSize: " + availableSize.ToString()); Debug.WriteLine("desiredSize: " + desiredSize.ToString()); return desiredSize; // 以下是自定義的 Measure 邏輯,供參考 /* Size childrenSize = new Size(0, 0); foreach (UIElement child in this.Children) { child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); childrenSize.Width += child.DesiredSize.Width; childrenSize.Height += child.DesiredSize.Height; } return childrenSize; */ } // 3、爸爸收到兒子們的反饋後,告訴兒子們自己最終提供的尺寸 finalSize protected override Size ArrangeOverride(Size finalSize) { // 4、兒子們根據 finalSize 安排各自的位置,然後爸爸的呈現尺寸也就確定了 renderSize Size renderSize = base.ArrangeOverride(finalSize); Debug.WriteLine("finalSize: " + finalSize.ToString()); Debug.WriteLine("renderSize: " + renderSize.ToString()); return renderSize; // 以下是自定義的 Arrange 邏輯,供參考 /* Point childPos = new Point(0, 0); foreach (UIElement child in this.Children) { child.Arrange(new Rect(childPos, new Size(child.DesiredSize.Width, child.DesiredSize.Height))); childPos.Y += child.RenderSize.Height; } return finalSize; */ } } } /* * 輸出結果: * availableSize: 200,200 * desiredSize: 150,250 * finalSize: 200,250 * renderSize: 200,250 */ /* * 注: * UIElement * 調用 Measure() 方法後會更新 DesiredSize 屬性 * 調用 Arrange() 方法後會更新 RenderSize 屬性 * UpdateLayout() - 強制 layout 遞歸更新 * * FrameworkElement - 繼承自 UIElement * MeasureOverride() - 重寫 Measure() * ArrangeOverride() - 重寫 Arrange() * ActualWidth 和 ActualHeight 來自 RenderSize,每次 UpdateLayout() 後都會被更新 */
2、演示如何獲取UI元素的位置信息
Controls/Basic/GeneralTransformDemo.xaml
<Page x:Class="XamlDemo.Controls.Basic.GeneralTransformDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.Basic" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <Grid HorizontalAlignment="Left" VerticalAlignment="Top"> <Rectangle Name="rectangle1" Width="300" Height="200" Fill="Red" /> <Rectangle Name="rectangle2" Width="150" Height="100" Fill="Green" /> </Grid> <TextBlock Name="lblMsg" FontSize="14.667" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
Controls/Basic/GeneralTransformDemo.xaml.cs
/* * 演示 GeneralTransform 的應用,可以通過 UIElement.TransformToVisual() 獲取 */ using System; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; namespace XamlDemo.Controls.Basic { public sealed partial class GeneralTransformDemo : Page { public GeneralTransformDemo() { this.InitializeComponent(); this.Loaded += TransformToVisual_Loaded; } void TransformToVisual_Loaded(object sender, RoutedEventArgs e) { lblMsg.Text = ""; Demo1(); lblMsg.Text += Environment.NewLine; Demo2(); } // 演示如何獲取 UIElement 相對於屏幕原點所占用的矩形區域 private void Demo1() { GeneralTransform generalTransform = rectangle1.TransformToVisual(null); // 獲取 rectangle1 相對於屏幕的 GeneralTransform Point point = generalTransform.TransformPoint(new Point(0, 0)); // rectangle1 的原點(左上角頂點)相對於屏幕 0,0 點的位置 Rect rect = new Rect(point, new Size(rectangle1.ActualWidth, rectangle1.ActualHeight)); lblMsg.Text += "紅色矩形相對於屏幕原點的位置:" + rect.ToString(); } // 演示如何獲取 UIElement 相對於另一個 UIElement 原點所占用的矩形區域 private void Demo2() { GeneralTransform generalTransform = rectangle1.TransformToVisual(rectangle2); // 獲取 rectangle1 相對於 rectangle2 的 GeneralTransform Point point = generalTransform.TransformPoint(new Point(0, 0)); // rectangle1 的原點(左上角頂點)相對於 rectangle2 的原點(左上角頂點)的位置 Rect rect = new Rect(point, new Size(rectangle1.ActualWidth, rectangle1.ActualHeight)); lblMsg.Text += "紅色矩形相對於綠色矩形左上角頂點的位置:" + rect.ToString(); } } }
3、演示 VisualTreeHelper 的應用
Controls/Basic/VisualTree.xaml
<Page x:Class="XamlDemo.Controls.Basic.VisualTree" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.Basic" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <Grid Name="container" HorizontalAlignment="Left" VerticalAlignment="Top" Tapped="container_Tapped_1"> <Rectangle Name="rectangle" Width="300" Height="200" Fill="Red" /> <Border Name="border" Width="200" Height="120" Background="Green" /> <ScrollViewer Name="scrollViewer" Width="150" Height="150" Background="Blue" /> </Grid> <TextBlock Name="lblMsg" FontSize="14.667" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
Controls/Basic/VisualTree.xaml.cs
/* * 演示 VisualTreeHelper 的應用 * * VisualTreeHelper - 訪問可視樹的幫助類 * GetChildrenCount(DependencyObject reference) - 獲取指定的元素內的子元素的數量 * DependencyObject GetChild(DependencyObject reference, int childIndex) - 獲取指定的元素內的,指定索引位置的子元素 * GetParent(DependencyObject reference) - 獲取指定的元素的父元素 * FindElementsInHostCoordinates(Point intersectingPoint, UIElement subtree, bool includeAllElements) - 查找某一點內的全部元素(包括控件模板內的元素) * intersectingPoint - 指定的點的坐標 * subtree - 在此元素內進行查找,包括此元素 * includeAllElements * true - 查找全部元素,包括 IsHitTestVisible 為 true 的和 IsHitTestVisible 為 false 的 * false - 僅查找 IsHitTestVisible 為 true 的元素 * FindElementsInHostCoordinates(Rect intersectingRect, UIElement subtree, bool includeAllElements) - 查找某一矩形區域內的全部元素(包括控件模板內的元素) * intersectingRect - 指定的矩形區域 * subtree - 在此元素內進行查找,包括此元素 * includeAllElements * true - 查找全部元素,包括 IsHitTestVisible 為 true 的和 IsHitTestVisible 為 false 的 * false - 僅查找 IsHitTestVisible 為 true 的元素 */ using System; using System.Collections.Generic; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Media; namespace XamlDemo.Controls.Basic { public sealed partial class VisualTree : Page { public VisualTree() { this.InitializeComponent(); this.Loaded += VisualTree_Loaded; } void VisualTree_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) { // 獲取 container 中包含的元素 lblMsg.Text = "container 中包含的元素有:"; int numVisuals = VisualTreeHelper.GetChildrenCount(container); for (int i = 0; i < numVisuals; i++) { DependencyObject element = VisualTreeHelper.GetChild(container, i); lblMsg.Text += Environment.NewLine; lblMsg.Text += element.GetType().ToString(); } lblMsg.Text += Environment.NewLine; lblMsg.Text += Environment.NewLine; // 在 scrollViewer 控件自身的模板中查找類型為 ScrollBar 的名為 VerticalScrollBar 的控件 lblMsg.Text += "查找 scrollViewer 中的名為“VerticalScrollBar”的 ScrollBar 控件:"; lblMsg.Text += Environment.NewLine; ScrollBar scrollBar = GetVisualChild<ScrollBar>(scrollViewer, "VerticalScrollBar"); if (scrollBar != null) lblMsg.Text += "找到了"; else lblMsg.Text += "未找到"; } private void container_Tapped_1(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { // 獲取鼠標單擊的位置,container 范圍內所包含的全部元素(包括控件模板內的元素) lblMsg.Text = "鼠標單擊的位置,container 內,包含的元素有:"; IEnumerable<UIElement> elementsPoint = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), container, true); var elementsPointEnumerator = elementsPoint.GetEnumerator(); while (elementsPointEnumerator.MoveNext()) { lblMsg.Text += Environment.NewLine; lblMsg.Text += elementsPointEnumerator.Current.GetType().ToString(); } lblMsg.Text += Environment.NewLine; lblMsg.Text += Environment.NewLine; // 獲取以鼠標單擊的位置為頂點,100*100 大小的矩形內,container 范圍內所包含的全部元素(包括控件模板內的元素) lblMsg.Text += "以鼠標單擊的位置為頂點,100*100 大小的矩形范圍內,container 內,包含的元素有:"; IEnumerable<UIElement> elementsRect = VisualTreeHelper.FindElementsInHostCoordinates(new Rect(e.GetPosition(null), new Size(100, 100)), container, true); var elementsRectEnumerator = elementsRect.GetEnumerator(); while (elementsRectEnumerator.MoveNext()) { lblMsg.Text += Environment.NewLine; lblMsg.Text += elementsRectEnumerator.Current.GetType().ToString(); } } /// <summary> /// 獲取指定元素內部的指定名稱的 FrameworkElement /// </summary> private T GetVisualChild<T>(DependencyObject parent, string name) where T : FrameworkElement { // T 是引用類型則為 null,T 是值類型則為 0 T child = default(T); int numVisuals = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < numVisuals; i++) { DependencyObject obj = VisualTreeHelper.GetChild(parent, i); child = obj as T; if (child == null || child.Name != name) child = GetVisualChild<T>(obj, name); if (child != null) break; } return child; } } }
OK
[源碼下載]:http://files.cnblogs.com/webabcd/Windows8.rar