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

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

[源码下载]


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



作者:webabcd


介绍
重新想象 Windows 8 Store Apps 之 控件基础

  • DependencyProperty - 依赖属性
  • AttachedProperty - 附加属性
  • 控件的继承关系
  • 路由事件和命中测试



示例
1、开发一个具有 DependencyProperty 和 AttachedProperty 的自定义控件
MyControls/themes/generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyControls">

    <!--
        自定义控件的样式在本文件(themes/generic.xaml)中定义
    
        整合外部 ResourceDictionary
    -->
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ms-appx:///MyControls/themes/MyControl.xaml"/>
    </ResourceDictionary.MergedDictionaries>
   
</ResourceDictionary>

MyControls/themes/MyControl.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyControls">
    <Style TargetType="local:MyControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                        <StackPanel>
                            <!--绑定自定义依赖属性-->
                            <TextBlock Text="{TemplateBinding Title}" Foreground="White" FontSize="26.667" />

                            <!--绑定自定义附加属性-->
                            <TextBlock Text="{TemplateBinding local:MyAttachedProperty.SubTitle}" Foreground="White" FontSize="14.667" />
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

MyControls/MyControl.cs

/*
 * 开发一个自定义控件,用于演示依赖属性(Dependency Property)和附加属性(Attached Property)
 * 
 * 依赖属性:可以用于样式, 模板, 绑定, 动画
 * 附加属性:全局可用的依赖属性
 */

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;

namespace MyControls
{
    /// <summary>
    /// 用于依赖属性的演示
    /// </summary>
    public class MyControl : Control
    {
        public MyControl()
        {
            // 指定默认样式为 typeof(MyControl),即对应:<Style xmlns:local="using:MyControls" TargetType="local:MyControl" />
            this.DefaultStyleKey = typeof(MyControl);
        }

        // 通过 DependencyObject.GetValue() 和 DependencyObject.SetValue() 访问依赖属性,这里由 Title 属性封装一下,以方便对依赖属性的访问
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // 注册一个依赖属性
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register(
                "Title", // 依赖属性的名称
                typeof(string),  // 依赖属性的数据类型
                typeof(MyControl),  // 依赖属性所属的类
                new PropertyMetadata("", PropertyMetadataCallback)); // 指定依赖属性的默认值,以及值发生改变时所调用的方法

        private static void PropertyMetadataCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            object newValue = args.NewValue; // 发生改变之后的值
            object oldValue = args.OldValue; // 发生改变之前的值
        }
    }



    /// <summary>
    /// 用于附加属性的演示
    /// </summary>
    public class MyAttachedProperty
    {
        // 获取附加属性
        public static string GetSubTitle(DependencyObject obj)
        {
            return (string)obj.GetValue(SubTitleProperty);
        }

        // 设置附加属性
        public static void SetSubTitle(DependencyObject obj, string value)
        {
            obj.SetValue(SubTitleProperty, value);
        }

        // 注册一个附加属性
        public static readonly DependencyProperty SubTitleProperty =
            DependencyProperty.RegisterAttached(
                "SubTitle", // 附加属性的名称
                typeof(string), // 附加属性的数据类型
                typeof(MyAttachedProperty), // 附加属性所属的类
                new PropertyMetadata("", PropertyMetadataCallback)); // 指定附加属性的默认值,以及值发生改变时所调用的方法

        private static void PropertyMetadataCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            object newValue = args.NewValue; // 发生改变之后的值
            object oldValue = args.OldValue; // 发生改变之前的值
        }
    }
}


2、演示依赖属性的使用
Controls/Basic/DependencyPropertyDemo.xaml

<Page
    x:Class="XamlDemo.Controls.Basic.DependencyPropertyDemo"
    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"
    xmlns:myControls="using:MyControls"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">
            
            <!--
                演示如何开发和使用自定义依赖属性(本例所用到的自定义控件请参看:MyControls/MyControl.cs)
            -->
            <myControls:MyControl Background="Blue" BorderBrush="Yellow" BorderThickness="1" Width="100" HorizontalAlignment="Left"
                                  Title="{Binding Value, ElementName=slider}" />

            <Slider Name="slider" Width="100" Minimum="0" Maximum="200" IsThumbToolTipEnabled="False"  HorizontalAlignment="Left" />

        </StackPanel>
    </Grid>
</Page>


3、演示附加属性的使用
Controls/Basic/AttachedPropertyDemo.xaml

<Page
    x:Class="XamlDemo.Controls.Basic.AttachedPropertyDemo"
    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"
    xmlns:myControls="using:MyControls"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <!--
                演示如何开发和使用自定义附加属性(本例所用到的自定义控件请参看:MyControls/MyControl.cs)
            -->
            <myControls:MyControl Background="Blue" BorderBrush="Yellow" BorderThickness="1" Width="100" HorizontalAlignment="Left"
                                  Title="{Binding Value, ElementName=slider}" myControls:MyAttachedProperty.SubTitle="{Binding Value, ElementName=slider}" />

            <Slider Name="slider" Width="100" Minimum="0" Maximum="200" IsThumbToolTipEnabled="False"  HorizontalAlignment="Left" />
            
        </StackPanel>
    </Grid>
</Page>


4、控件的继承关系的概述
Controls/Basic/Inherit.xaml

<Page
    x:Class="XamlDemo.Controls.Basic.Inherit"
    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">

            <TextBlock Name="lblMsg" FontSize="14.667" LineHeight="25">
                <Run>继承关系: FrameworkElement -> UIElement -> DependencyObject</Run>
                <LineBreak />
                <Run>DependencyObject - 提供对依赖属性的访问,以及获取此对象关联的 CoreDispatcher</Run>
                <LineBreak />
                <Run>UIElement - 可视元素,键盘和鼠标输入等</Run>
                <LineBreak />
                <Run>FrameworkElement - 框架元素,数据绑定,一些公共 API 等。例:Control, TextBlock, WebView 等继承自 FrameworkElement</Run>
                <LineBreak />
                <Run>ContentControl - 其内包含有一个内容,继承自 Control。例:ScrollViewer, AppBar 等继承自 ContentControl</Run>
                <LineBreak />
                <Run>ButtonBase - 按钮的基本功能,继承自 ContentControl。例:Button, RepeatButton 等继承自 ButtonBase</Run>
                <LineBreak />
                <Run>ToggleButton - 可切换状态的按钮,继承自 ButtonBase。例:RadioButton, CheckBox 等继承自 ToggleButton</Run>
                <LineBreak />
                <Run>RangeBase - 值在某一范围内,继承自 ButtonBase。例:ProgressBar, Slider, ScrollBar 等继承自 RangeBase</Run>
                <LineBreak />
                <Run>ItemsControl - 用于呈现集合,继承自 Control</Run>
                <LineBreak />
                <Run>Selector - 可选择集合中的某一项,继承自 ItemsControl。例:ComboBox, ListBox, FlipView, ListViewBase 等继承自 Selector</Run>
                <LineBreak />
                <Run>ListViewBase - 继承自 ListViewBase 的控件有 GridView 和 ListView</Run>
                <LineBreak />
                <Run>Panel - 一个容器,继承自 FrameworkElement。例:Grid, StackPanel, Canvas 等继承自 Panel</Run>
                <LineBreak />
                <Run>如 ScrollBar, Thumb, RangeBase, ButtonBase, Selector, Popup 等这类基元控件在 Windows.UI.Xaml.Controls.Primitives 命名空间下</Run>
            </TextBlock>

        </StackPanel>
    </Grid>
</Page>


5、路由事件和命中测试
Controls/Basic/RoutedEventDemo.xaml

<Page
    x:Class="XamlDemo.Controls.Basic.RoutedEventDemo"
    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">
                <!--
                    演示事件冒泡:儿子传递事件给爸爸,爸爸传递事件给爷爷,这就是事件冒泡
                -->
                <Border Name="borderRed" Background="Red" Width="300" Height="300">
                    <Border Name="borderGreen" Background="Green" Width="250" Height="250" Tapped="borderGreen_Tapped_1">
                        <Border Name="borderBlue" Background="Blue" Width="200" Height="200" Tapped="borderBlue_Tapped_1">
                            <Border Name="borderOrange" Background="Orange" Width="150" Height="150" Tapped="borderOrange_Tapped_1">
                                <!--命中测试不可见,也就是说 borderPurple 和 borderYellow 均命中测试不可见-->
                                <Border Name="borderPurple" Background="Purple" Width="100" Height="100" Tapped="borderPurple_Tapped_1" IsHitTestVisible="False">
                                    <Border Name="borderYellow" Background="Yellow" Width="50" Height="50" Tapped="borderYellow_Tapped_1" />
                                </Border>
                            </Border>
                        </Border>
                    </Border>
                </Border>
                
                <!--
                    像这样排列元素,是没有事件冒泡的,而只是前面的元素响应事件,后面的元素不会响应事件,也就是说同辈间没有事件冒泡的概念
                    IsHitTestVisible - 是否对命中测试可见
                    <Rectangle Name="rectangle1" Width="800" Height="400" Fill="Red" />
                    <Rectangle Name="rectangle2" Width="700" Height="350" Fill="Green" />
                    <Rectangle Name="rectangle3" Width="600" Height="300" Fill="Blue" />
                    <Rectangle Name="rectangle4" Width="500" Height="250" Fill="Orange" />
                    <Rectangle Name="rectangle5" Width="400" Height="200" Fill="Purple" />
                -->
            </Grid>

            <TextBlock Name="lblMsg" FontSize="14.667" Margin="0 10 0 0" />

        </StackPanel>
    </Grid>
</Page>

Controls/Basic/RoutedEventDemo.xaml.cs

/*
 * 演示路由事件的冒泡和命中测试的可见性
 * 
 * TappedRoutedEventArgs
 *     OriginalSource - 引发此路由事件的对象
 *     Handled - 是否将路由事件标记为已处理
 *         true - 不再冒泡
 *         false - 继续冒泡
 *     
 * UIElement
 *     IsHitTestVisible - 是否对命中测试可见
 */

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

namespace XamlDemo.Controls.Basic
{
    public sealed partial class RoutedEventDemo : Page
    {
        public RoutedEventDemo()
        {
            this.InitializeComponent();

            // AddHandler() - 注册一个路由事件,注意最后一个参数:true 代表即使子辈 TappedRoutedEventArgs.Handled = true 也不会影响此元素事件的触发
            // RemoveHandler() - 移除指定的路由事件
            borderRed.AddHandler(UIElement.TappedEvent, new TappedEventHandler(borderRed_Tapped_1), true);
        }

        private void borderRed_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            lblMsg.Text += "borderRed tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;
        }

        private void borderGreen_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            lblMsg.Text += "borderGreen tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;
        }

        private void borderBlue_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            lblMsg.Text += "borderBlue tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;

            // 不会再冒泡,也就是说 borderGreen 无法响应 Tapped 事件,但是 borderRed 注册 Tapped 事件时 handledEventsToo = true,所以 borderRed 会响应 Tapped 事件
            e.Handled = true;
        }

        private void borderOrange_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            lblMsg.Text += "borderOrange tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;
        }

        private void borderPurple_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            // 不会响应此事件,因为 borderPurple 的 IsHitTestVisible = false
            lblMsg.Text += "borderPurple tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;
        }

        private void borderYellow_Tapped_1(object sender, TappedRoutedEventArgs e)
        {
            // 不会响应此事件,因为 borderYellow 的爸爸 borderPurple 的 IsHitTestVisible = false
            lblMsg.Text += "borderYellow tapped, originalSource: " + (e.OriginalSource as FrameworkElement).Name;
            lblMsg.Text += Environment.NewLine;
        }
    }
}



OK
[源码下载]

目录
相关文章
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
2824 4
|
机器学习/深度学习 人工智能 监控
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
|
11月前
|
存储 人工智能 编译器
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
747 11
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
|
人工智能 监控 JavaScript
模拟依赖关系和 AI 是Vue.js测试的下一个前沿领域
模拟依赖关系和 AI 是Vue.js测试的下一个前沿领域
172 1
|
Java 测试技术 Maven
单元测试问题之在Maven项目中引入JUnit 5和Mockito的依赖如何解决
单元测试问题之在Maven项目中引入JUnit 5和Mockito的依赖如何解决
1158 1
|
Java 测试技术 数据库
单元测试问题之LoginServiceImpl以避免对IpUtil的依赖问题如何解决
单元测试问题之LoginServiceImpl以避免对IpUtil的依赖问题如何解决
127 4
|
运维 Java 测试技术
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
219 3
|
存储 测试技术 Python
记一次线上安全测试中误用父类属性导致数据污染的解决方案
在线上安全测试的过程中,会使用 Nmap 进行端口扫描,为了提升端口扫描的效率,扫描策略通常是检测常用端口是否处于开放状态,并在父类中使用名为 all_open_ports 的属性来记录这些开放的端口。 在后续的测试过程中,需要检查所涉及的端口是否包含在 all_open_ports 中。如果不存在,就需要进一步对这些端口进行开放检测。如果端口的检测结果是开放的,测试将继续进行并将这些端口记录到 all_open_ports 中,以便在下次遇到相同端口时无需重复检测。 然而,由于安全测试是多线程进行的,某些情况下可以将 all_open_ports 理解为共享变量,这导致当两个不同的测试环境同
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作