| 
 | 
	
 
 
  [源码下载] 
 
 
 
 
重新想象 Windows 8 Store Apps (17) - 控件基础: Measure, Arrange, GeneralTransform, VisualTree   
作者:webabcd 
 
介绍 
重新想象 Windows 8 Store Apps 之 控件基础 
 
 
- Measure() 和 Arrange() - xaml 的 layout 系统
 
 - GeneralTransform - 通过 UIElement.TransformToVisual() 获取元素的位置信息
 
 - VisualTree - 可视树
 
     
示例 
1、演示 xaml 的 layout 系统 
Controls/Basic/MeasureArrange.xaml 
 
 
 
 
 
 
 
 
 
 
 
  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 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  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 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  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(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 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 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(); 
} 
} 
///  
/// 获取指定元素内部的指定名称的 FrameworkElement 
///  
private T GetVisualChild(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(obj, name); 
if (child != null) 
break; 
} 
return child; 
} 
} 
} 
   
OK 
[源码下载] |   
 
 
 
 | 
  
 |