[源码下载]

背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理

作者:webabcd

介绍
背水一战 Windows 10 之 控件(自定义控件)

  • 自定义控件的 Layout 系统
  • 自定义控件的控件模板和事件处理的相关知识点

示例
1、演示自定义控件的 Layout 系统
/MyControls/MyControl2.cs

/*
* 本例通过一个自定义控件来演示 uwp 中可视元素的 Layout 系统
*
* uwp 的 layout 是一个递归系统,本 demo 就递归的一个过程做说明(步骤顺序参见代码注释中的序号)
*
*
* Measure() 的作用是测量尺寸
* Arrange() 的作用是排列元素
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using Windows.Foundation;
using System;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic; namespace MyControls
{
/// <summary>
/// 一个每行都会自动缩进的 Panel
/// </summary>
public sealed class MyControl2 : Panel
{
// 相对上一行的缩进值
const double INDENT = ; public MyControl2()
{ } // 1、首先爸爸知道自己能够提供的尺寸 availableSize,然后告诉儿子们
protected override Size MeasureOverride(Size availableSize) // 测量出期待的尺寸并返回
{
// 2、儿子们收到 availableSize 后,又结合了自身的实际情况,然后告诉爸爸儿子们所期望的尺寸 desiredSize
List<double> widthList = new List<double>();
Size desiredSize = new Size(, );
foreach (UIElement child in this.Children)
{
// 如果 child 是 FrameworkElement 的话,则当调用其 Measure() 方法时会自动调用其 MeasureOverride() 方法
child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
widthList.Add(child.DesiredSize.Width);
desiredSize.Height += child.DesiredSize.Height;
} if (this.Children.Count > )
{
desiredSize.Width = widthList.Max();
desiredSize.Width += INDENT * (this.Children.Count - );
} Debug.WriteLine("availableSize: " + availableSize.ToString());
Debug.WriteLine("desiredSize: " + desiredSize.ToString()); return desiredSize;
} // 3、爸爸收到儿子们的反馈后,告诉儿子们自己最终提供的尺寸 finalSize
protected override Size ArrangeOverride(Size finalSize) // 排列元素,并返回呈现尺寸
{
// 4、儿子们根据 finalSize 安排各自的位置,然后爸爸的呈现尺寸也就确定了 renderSize
Point childPosition = new Point(, );
foreach (UIElement child in this.Children)
{
// 如果 child 是 FrameworkElement 的话,则当调用其 Arrange() 方法时会自动调用其 ArrangeOverride() 方法
child.Arrange(new Rect(childPosition, new Size(child.DesiredSize.Width, child.DesiredSize.Height)));
childPosition.X += INDENT;
childPosition.Y += child.DesiredSize.Height;
} Size renderSize = new Size(, );
renderSize.Width = finalSize.Width;
renderSize.Height = childPosition.Y; Debug.WriteLine("finalSize: " + finalSize.ToString());
Debug.WriteLine("renderSize: " + renderSize.ToString()); return finalSize;
}
}
} /*
* 输出结果如下(运行 /Controls/CustomControl/Demo2.xaml 示例)
* availableSize: 800,Double.PositiveInfinity
* desiredSize: 141,120
* finalSize: 800,120
* renderSize: 800,120
*/ /*
* 注:
* UIElement
* 调用 Measure() 方法后会更新 DesiredSize 属性
* 调用 Arrange() 方法后会更新 RenderSize 属性
* UpdateLayout() - 强制 layout 递归更新
*
* FrameworkElement - 继承自 UIElement
* MeasureOverride() - 在 Measure() 中自动调用
* ArrangeOverride() - 在 Arrange() 中自动调用
* ActualWidth 和 ActualHeight 来自 RenderSize,每次 UpdateLayout() 后都会被更新
*/ /*
* 注:
* 1、uwp 的 layout 是一个递归系统
* 2、UIElement 的 InvalidateMeasure() 就是递归调用自己和子辈门的 Measure()
* 3、UIElement 的 InvalidateArrange() 就是递归调用自己和子辈门的 Arrange()
*
* 一个通过 uwp 自带控件说明 layout 的示例,请参见:/Controls/BaseControl/UIElementDemo/LayoutDemo.xaml.cs
*/

Controls/CustomControl/Demo2.xaml

<Page
x:Class="Windows10.Controls.CustomControl.Demo2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows10.Controls.CustomControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xmlns:myControls="using:MyControls"> <Grid Background="Transparent">
<StackPanel Margin="10 0 10 10"> <!--
演示元素的 Layout 系统
本例所用到的自定义控件请参看:MyControls/MyControl2.cs
-->
<myControls:MyControl2 Margin="5" Background="Orange" HorizontalAlignment="Left" Width="800">
<myControls:MyControl2.Children>
<TextBlock Text="aaaaaaaa" Margin="5" />
<TextBlock Text="bbbbbbbb" Margin="5" />
<TextBlock Text="cccccccc" Margin="5" />
<TextBlock Text="dddddddd" Margin="5" />
</myControls:MyControl2.Children>
</myControls:MyControl2> </StackPanel>
</Grid>
</Page>

Controls/CustomControl/Demo2.xaml.cs

/*
* 本例用于演示元素的 Layout 系统
*/ using Windows.UI.Xaml.Controls; namespace Windows10.Controls.CustomControl
{
public sealed partial class Demo2 : Page
{
public Demo2()
{
this.InitializeComponent();
}
}
}

2、演示自定义控件的控件模板和事件处理的相关知识点
/MyControls/themes/MyControl3.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:MyControl3">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyControl3"> <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<TextBlock Name="textBlock" Foreground="White" FontSize="24" />
</StackPanel> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="Green" />
</Storyboard>
</VisualState> <VisualStateGroup.Transitions>
<VisualTransition To="PointerOver" GeneratedDuration="0:0:1">
<VisualTransition.GeneratedEasingFunction>
<ElasticEase EasingMode="EaseInOut" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border> </ControlTemplate>
</Setter.Value>
</Setter>
</Style> </ResourceDictionary>

/MyControls/MyControl3.cs

/*
* 开发一个自定义控件,用于演示控件模板和事件处理的相关知识点
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Input; namespace MyControls
{
/// <summary>
/// 自定义控件
/// </summary>
public sealed class MyControl3 : Control
{
public MyControl3()
{
this.DefaultStyleKey = typeof(MyControl3);
} // ApplyTemplate() - 强制加载控件模板,一般不用调用(因为控件模板会自动加载)。有一种使用场景是:当父控件应用控件模板时要求子控件必须先应用控件模板以便父控件使用时,则可以先调用子控件的此方法
// GetTemplateChild() - 查找控件模板中的指定名字的元素
// override OnApplyTemplate() - 应用控件模板时调用
protected override void OnApplyTemplate()
{
base.OnApplyTemplate(); TextBlock textBlock = (TextBlock)GetTemplateChild("textBlock");
if (this.Background is SolidColorBrush)
{
textBlock.Text = $"background: {((SolidColorBrush)this.Background).Color}";
} VisualStateManager.GoToState(this, "Normal", false);
} // override GoToElementStateCore() - VisualState 转换时调用(此方法仅在自定义 ContentPresenter 并将其应用于 GridView 或 ListView 的 ItemContainerStyle 时才会被调用)
// 参见:/Controls/CollectionControl/ItemsControlDemo/MyItemPresenter.cs
protected override bool GoToElementStateCore(string stateName, bool useTransitions)
{
return base.GoToElementStateCore(stateName, useTransitions);
} // 在 Control 中有很多可 override 的事件处理方法,详见文档
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "PointerOver", true);
} protected override void OnPointerExited(PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", false);
}
}
}

Controls/CustomControl/Demo3.xaml

<Page
x:Class="Windows10.Controls.CustomControl.Demo3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows10.Controls.CustomControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xmlns:myControls="using:MyControls"> <Grid Background="Transparent">
<StackPanel Margin="10 0 10 10"> <!--
演示自定义控件的控件模板和事件处理的相关知识点
本例所用到的自定义控件请参看:MyControls/MyControl3.cs
-->
<myControls:MyControl3 Background="Blue" BorderBrush="Yellow" BorderThickness="1" HorizontalAlignment="Left" Margin="5" /> </StackPanel>
</Grid>
</Page>

Controls/CustomControl/Demo3.xaml.cs

/*
* 本例用于演示自定义控件的控件模板和事件处理的相关知识点
*/ using Windows.UI.Xaml.Controls; namespace Windows10.Controls.CustomControl
{
public sealed partial class Demo3 : Page
{
public Demo3()
{
this.InitializeComponent();
}
}
}

OK
[源码下载]

背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理的更多相关文章

  1. 背水一战 Windows 10 (78) - 自定义控件: 基础知识, 依赖属性, 附加属性

    [源码下载] 背水一战 Windows 10 (78) - 自定义控件: 基础知识, 依赖属性, 附加属性 作者:webabcd 介绍背水一战 Windows 10 之 控件(自定义控件) 自定义控件 ...

  2. 背水一战 Windows 10 (75) - 控件(控件基类): FrameworkElement - 基础知识, 相关事件, HorizontalAlignment, VerticalAlignment

    [源码下载] 背水一战 Windows 10 (75) - 控件(控件基类): FrameworkElement - 基础知识, 相关事件, HorizontalAlignment, Vertical ...

  3. 背水一战 Windows 10 (72) - 控件(控件基类): UIElement - UIElement 的位置, UIElement 的布局, UIElement 的其他特性

    [源码下载] 背水一战 Windows 10 (72) - 控件(控件基类): UIElement - UIElement 的位置, UIElement 的布局, UIElement 的其他特性 作者 ...

  4. 背水一战 Windows 10 (37) - 控件(弹出类): MessageDialog, ContentDialog

    [源码下载] 背水一战 Windows 10 (37) - 控件(弹出类): MessageDialog, ContentDialog 作者:webabcd 介绍背水一战 Windows 10 之 控 ...

  5. 背水一战 Windows 10 (29) - 控件(文本类): RichTextBlock, RichTextBlockOverflow, RichEditBox

    [源码下载] 背水一战 Windows 10 (29) - 控件(文本类): RichTextBlock, RichTextBlockOverflow, RichEditBox 作者:webabcd ...

  6. 背水一战 Windows 10 (7) - 控件 UI: VisualState, VisualStateManager, 控件的默认 UI

    [源码下载] 背水一战 Windows 10 (7) - 控件 UI: VisualState, VisualStateManager, 控件的默认 UI 作者:webabcd 介绍背水一战 Wind ...

  7. 背水一战 Windows 10 (76) - 控件(控件基类): Control - 基础知识, 焦点相关, 运行时获取 ControlTemplate 和 DataTemplate 中的元素

    [源码下载] 背水一战 Windows 10 (76) - 控件(控件基类): Control - 基础知识, 焦点相关, 运行时获取 ControlTemplate 和 DataTemplate 中 ...

  8. 背水一战 Windows 10 (74) - 控件(控件基类): UIElement - 与 CanDrag 相关的事件, 与 AllowDrop 相关的事件

    [源码下载] 背水一战 Windows 10 (74) - 控件(控件基类): UIElement - 与 CanDrag 相关的事件, 与 AllowDrop 相关的事件 作者:webabcd 介绍 ...

  9. 背水一战 Windows 10 (73) - 控件(控件基类): UIElement - 拖放的基本应用, 手动开启 UIElement 的拖放操作

    [源码下载] 背水一战 Windows 10 (73) - 控件(控件基类): UIElement - 拖放的基本应用, 手动开启 UIElement 的拖放操作 作者:webabcd 介绍背水一战 ...

随机推荐

  1. laravel表单操作

    $request->all()//获取所有参数if($request->isMethod('GET')){判断是否是GET请求}$res = $request->is('studen ...

  2. Android向系统日历中添加日程事件

    转自Android向系统日历中添加日程事件 总结 在项目开发中,我们有预约提醒.定时提醒需求时,可以使用系统日历来辅助提醒: 通过向系统日历中写入事件.设置提醒方式(闹钟),实现到时间自动提醒的功能: ...

  3. 0初识Linux

    今天三八妇女节,Linux就该这么学,开课第一天.信心满满,激动,期待,要努力了.(博客为预习写的,今天又做了更新.)   Linux第一印象就是黑色背景屏幕,上面还有好多代码,敲的一手好的命令操控着 ...

  4. ==、equals和hashCode小结

    1.== ==是关系操作符,对于基本类型(byte,short,char,int,long,float,double,boolean),比较的是值是否相等:对于对象,比较的是对象的引用(也即栈内存中的 ...

  5. JVM系列2:垃圾收集器与内存分配策略

    垃圾收集是一个很大话题,本文也只是看了深入理解Java虚拟机总结了下垃圾收集的知识. 首先按照惯例,先上思维导图: 垃圾收集简而言之就是JVM帮我们清理掉内存区域不需要的数据.它主要负责清理堆中实例对 ...

  6. 通过windows远程桌面连接CentOS系统

    前提: CentOS安装桌面,如果无桌面,请执行 # yum -y groups install "GNOME Desktop" # startx 1 2 配置源 # yum in ...

  7. 复用微信小程序源码包后仍然有原小程序的版本管理怎么处理

    前言: 复用微信小程序源码包后,重新创建项目导入源码包,会发现开发者工具版本管理中仍然有原来小程序的版本,这样就不太好了.毕竟是一个新的小程序,需要有新的版本控制的.那么这个问题怎么处理呢? 解决方案 ...

  8. java_16Arrays类

    1sort():对数组进行升序排列 public static void main(String[] args) { int[] arr= {2,43,6,7}; Arrays.sort(arr); ...

  9. linux主机名设置

    有时会报错: 代理抛出异常错误: java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostExcept ...

  10. Eclipse设置智能提示

    1.解决智能感知提示响应时间,使Eclipse追上VS的响应步伐:Window→Preferences→Java→Editor→Content Assist 这里的Auto activation de ...