[源码下载]

背水一战 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. hadoop 二次排序的思考

    name sorce jx 10 gj 15 jx 20 gj 30 1. 二次排序 key如何定义 2. grouping 是不是一定要实现,不实现可以吗? 3. 二次排序的本质是什么 4. 如果以 ...

  2. python3中一句话定义函数

    import math as marea=lambda r:r**2*m.pi #定义一个计算圆的面积的函数area(8) 显示结果 201.06192982974676

  3. maven的单元测试中没有

    原因:BaseTest没有找到单元测试造成的 增加一个空的单元测试 @Testpublic void testNothing(){} 异常现象:在maven项目执行mvn install 或mvn t ...

  4. 活用RPM获取包的信息

    rpm -q 功效大 如果你想要在系统上安装.卸载或是升级软件,需要对系统软件进行查询:或是有如下的场景: 安装了一个软件,需要知道这个软件的版本. 遇到一个文件,不认识它,需要知道它是什么软件,有什 ...

  5. c++符号常量:limits头文件

    CHAR_BIT char的位数 CHAR_MAX char的最大值 CHAR_MIN char的最小值 SCHAR_MAX signed char的最大值 SCHR_MIN signedchar的最 ...

  6. python open函数的坑

    python的open函数用来打开文件,但是在打开windows下文件时候会出错 f = open("e:\python_learn\test.txt", "r" ...

  7. CodeSmith 代码生成器

    在上一篇我们已经用PowerDesigner创建好了需要的测试数据库,下面就可以开始用它完成批量代码生成的工作啦. 下面我会一步步的解释如何用CodeSmith实现预期的结果的,事先声明一下,在此只做 ...

  8. typedef typename

    所以根据上述两条分析,  typedef typename RefBase::weakref_type weakref_type; 语句的真是面目是: typedef创建了存在类型的别名,而typen ...

  9. [Ting's笔记Day5]在部署到Heroku之前,将Rails项目从SQLite设定为PostgreSQL

    前情提要: Paas(平台及服务)公司Heroku是个可以把我们写好的App部署到网际网络的好地方.而本篇是我从自己的上一篇文章:将Ruby on Rails项目部署到Heroku遇到的问题,当时困扰 ...

  10. javaMail实现收发邮件(三)

    JavaMail API中定义了一个java.mail.Transport类,它专门用于执行邮件发送任务,这个类的实例对象封装了某种邮件发送协议的底层实施细节,应用程序调用这个类中的方法就可以把Mes ...