原文:重新想象 Windows 8 Store Apps (17) - 控件基础: Measure, Arrange, GeneralTransform, VisualTree

[源码下载]

重新想象 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

<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(, )); // 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(, )); // 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);
            ; 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(, )), 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);
            ; 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
[源码下载]

重新想象 Windows 8 Store Apps (17) - 控件基础: Measure, Arrange, GeneralTransform, VisualTree的更多相关文章

  1. 重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试

    原文:重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试 [源码下载] 重新想象 Windows 8 Store ...

  2. 重新想象 Windows 8 Store Apps (4) - 控件之提示控件: ProgressRing; 范围控件: ProgressBar, Slider

    原文:重新想象 Windows 8 Store Apps (4) - 控件之提示控件: ProgressRing; 范围控件: ProgressBar, Slider [源码下载] 重新想象 Wind ...

  3. 重新想象 Windows 8 Store Apps (15) - 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState, VisualStateManager

    原文:重新想象 Windows 8 Store Apps (15) - 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState ...

  4. 重新想象 Windows 8 Store Apps (14) - 控件 UI: RenderTransform, Projection, Clip, UseLayoutRounding

    原文:重新想象 Windows 8 Store Apps (14) - 控件 UI: RenderTransform, Projection, Clip, UseLayoutRounding [源码下 ...

  5. 重新想象 Windows 8 Store Apps (13) - 控件之 SemanticZoom

    原文:重新想象 Windows 8 Store Apps (13) - 控件之 SemanticZoom [源码下载] 重新想象 Windows 8 Store Apps (13) - 控件之 Sem ...

  6. 重新想象 Windows 8 Store Apps (12) - 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示

    原文:重新想象 Windows 8 Store Apps (12) - 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示 [源码下载] 重新想象 Windows 8 Store Ap ...

  7. 重新想象 Windows 8 Store Apps (11) - 控件之 ListView 和 GridView

    原文:重新想象 Windows 8 Store Apps (11) - 控件之 ListView 和 GridView [源码下载] 重新想象 Windows 8 Store Apps (11) - ...

  8. 重新想象 Windows 8 Store Apps (10) - 控件之 ScrollViewer 特性: Chaining, Rail, Inertia, Snap, Zoom

    原文:重新想象 Windows 8 Store Apps (10) - 控件之 ScrollViewer 特性: Chaining, Rail, Inertia, Snap, Zoom [源码下载] ...

  9. 重新想象 Windows 8 Store Apps (9) - 控件之 ScrollViewer 基础

    原文:重新想象 Windows 8 Store Apps (9) - 控件之 ScrollViewer 基础 [源码下载] 重新想象 Windows 8 Store Apps (9) - 控件之 Sc ...

随机推荐

  1. 简单封装分页功能pageView.js

    分页是一个很简单,通用的功能.作为一个有经验的前端开发人员,有义务把代码中类似这样公共的基础性的东西抽象出来,一来是改善代码的整体质量,更重要的是为了将来做类似的功能或者类似的项目,能减少不必要的重复 ...

  2. 【转】JAVA自学之路

    JAVA自学之路 一: 学会选择 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3G.测试等. 那么究竟应该选择什么方向 ...

  3. WCF Binding

    <Programming WCF Services>有一幅图也能说明各自的特征: 下面的图给出了我们选择Binding的方式

  4. JPA,EclipseLink 缓存机制学习(一) 树节点搜索问题引发的思考

    最近在项目在使用JPA+EclipseLink 的方式进行开发,其中EclipseLink使用版本为2.5.1.遇到一些缓存方面使用不当造成的问题,从本篇开始逐步学习EclipseLink的缓存机制. ...

  5. 关于JAVA的String类的一些方法

    一.得到字符串对象的有关信息 1.通过调用length()方法得到String的长度. String str=”This is a String”; int len =str.length(); 2. ...

  6. 关于js中onclick字符串传参问题

    规则: 外变是“”,里面就是‘’外边是‘’,里边就是“”   示例: var a="111"; var html="<a onclick='selecthoods( ...

  7. RabbitMQ服务端配置详解

    RabbitMQ支持三种配置方式: 1) 读取环境变量中配置, 这包括shell中环境变量和rabbitmq-env.conf/rabbitmq-env-conf.bat文件中配置的环境变量 可配置如 ...

  8. 自学Linux Shell6.2-用户自定义环境变量

    点击返回 自学Linux命令行与Shell脚本之路 6.2-用户自定义环境变量 1.设置局部用户定义变量 一旦启动bash shell(或者执行一个shell脚本),你就能创建这个shell进程可见的 ...

  9. zabbix 监控openshift pod状态

    需求: pod中的容器重启一次则报警通知 pod非Runing 状态则报警 pod中的容器非true状态则报警 三个需求其实是有点重叠的 pod重启期间pod肯定会有非Running状态,只要有重启报 ...

  10. node.js初识09

    1.node_module文件夹 如果你的require中没有写./,那么Node.js将该文件视为node_modules目录下的一个文件. 2.package.json文件 如果使用文件夹来统筹管 ...