[源码下载]

背水一战 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. python下彻底解决浏览器多标签打开与切换问题

    #coding:utf-8#Right_key_click and Tad switch#by dengpeiyou date:2018-7-7from selenium import webdriv ...

  2. Delphi中Chrome Chromium、Cef3学习笔记(三)

    原文   http://blog.csdn.net/xtfnpgy/article/details/46635871   Delphi与JS的交互问题: 一.执行简单的JS 上一篇已经讲过: chrm ...

  3. 转:解决AndroidStudio连不上Android设备真机的问题

    Android手机开发Android应用的时候,需要连接真机,进行应用软件的真机调试,但是由于诸多原因,可能导致无法与实现连接: 在我们连接了Android设备出现上面这种情况的时候,可以打开设备管理 ...

  4. Delphi TXLSReadWriteII 导出EXCEL

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  5. week07 13.1 NewsPipeline之 一 NewsMonitor

    我们要重构一下代码 因为我们之前写了utils 我们的NewsPipeline部分也要用到 所以我们把他们单独独立得拿出来 删掉原来的 将requirements.txt也拿出去 现在我们搬家完成 我 ...

  6. Intersect交集Except差集Union并集实例

    int[] oldArray = { 1, 2, 3, 4, 5 };int[] newArray = { 2, 4, 5, 7, 8, 9 };var jiaoJi = oldArray.Inter ...

  7. 2019.3.28 S21 day02pyth笔记总结

    昨日内容补充: 1.字符串:'中国'    'Hello' 字符:中是一个字符,e是一个字符 字节:中是3个字节,e是1个字节 位:01010101是8位,其中0或1分别是1位 unicode用于内存 ...

  8. ide调试

    F8:  程序向下执行一行(如果当前行有方法调用,这个方法将被执行完毕返回,然后到下一行) F7: 程序向下执行一行.如果该行有自定义方法,则运行进入自定义方法(不会进入官方类库的方法) Alt + ...

  9. C# 导出dataGridView中的值到Excel

    C# 怎么导出dataGridView中的值到Excel 1 2 3 4 5 6 在系统应用过程中,数据是系统的核心.如果直接在应用软件中看数据,有时也有些不便,所以就会把系统数据转换成Excel格式 ...

  10. ES6中的let命令

    ES6新增了let命令,用于声明变量.其用法类似var,区别是使用let命令声明的变量只在当前代码块有效. for循环的计数器就很适合使用let命令. var arr= [1,2,3,4,5]; fo ...