2018-10-22-win10-uwp-自定义控件入门
title | author | date | CreateTime | categories |
---|---|---|---|---|
win10 uwp 自定义控件入门
|
lindexi
|
2018-10-22 09:47:54 +0800
|
2018-10-21 15:52:11 +0800
|
Win10 UWP
|
本文告诉大家如何在 UWP 使用 CustomControl 自定义控件,在 UWP 的自定义控件的中文翻译是模板化控件,通过自定义控件可以完全控制整个控件的布局和渲染。
默认创建的自定义控件是没有带 xaml 的,如果想要让 CustomControl 可以使用 xaml 就需要引入主题的方法
下面就来告诉大家如何使用 xaml 来做界面
在 CustomControl 使用 xaml 写界面
在 UWP 主要的元素就是控件,可以说,整个 UWP 的界面都依靠控件画出来的。使用 xaml 可以快速画出好看的界面,而默认创建的 自定义控件和用户控件不一样,用户控件会带一个 xaml 直接修改就可以在设计器看到界面。
通过创建一个类继承 Control 类,我这里创建的是一个 Board 类
public sealed class Board : Control
然后在相同的文件夹,创建一个资源字典 Board.xaml 这样可以对应资源字典和创建的控件
在资源字典先引用命名控件,我这里创建 Board 是在 lindexi.UWP.Framework 命名空间,就需要在资源字典引用xmlns:local="using:lindexi.UWP.Framework"
这样才可以拿到对应的控件
namespace lindexi.UWP.Framework
{
public sealed class Board : Control
{ }
}
添加一个 Style 指定为刚才创建的 Board 控件
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:lindexi.UWP.Framework">
<Style TargetType="local:Board"> </Style>
</ResourceDictionary>
在这里不添加 Key 就是默认所有的 Board 控件都使用这个样式,通过修改 Template 的方法添加控件
<Style TargetType="local:Board">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Board">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> </Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
通过 ControlTemplate 的方法里面就和用户控件一样可以使用 xaml 写出界面,我这里就放一个 ContentControl 可以来定制
可以使用 ContentControl 的 Content 属性放入任意的 UIElement 都可以加入视觉树
<Style TargetType="local:Board">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Board">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ContentControl x:Name="ContentControl"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
但是现在的代码还没完成,还需要在项目创建一个 Theme 文件夹,然后在这个文件夹里面添加 Generic.xaml 资源字典,从这个字典引用刚才创建的 Board 资源字典,才可以在使用的时候找到
在 Generic.xaml 资源字典只需要添加下面的代码
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:lindexi.github.io"> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///lindexi/Framework/Board.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
需要注意 ResourceDictionary 的路径,修改为自己实际的控件的 xaml 文件的路径,注意这里必须使用 ms-appx:///
开头,文件使用的是相对于项目的路径,如果使用的是相对于这个文件的路径,就会在运行的时候,在某个类的构造函数告诉
Failed to assign to property 'Windows.UI.Xaml.ResourceDictionary.Source' because the type 'Windows.Foundation.String' cannot be assigned to the type 'Windows.Foundation.Uri'.
虽然现在设置好了控件的 xaml 但是现在的 xaml 没有内容,需要在 Board 类添加一些代码,让大家可以看到自己的 xaml 是否可以在 Board 使用
首先是添加 TemplatePart 在 Board 类,这样是在约定在 xaml 界面需要添加一个对应的控件,指定了控件的 Name 和这是一个什么控件
[TemplatePart(Name = "ContentControl", Type = typeof(ContentControl))]
public sealed class Board : Control
是否记得在 Board 的资源字典就写了一个 ContentControl 类,虽然添加了约定但是还是需要将这个控件拿出来,通过重写 OnApplyTemplate 方法就可以使用 GetTemplateChild 方法拿到 xaml 里写的控件
protected override void OnApplyTemplate()
{
Content = (ContentControl) GetTemplateChild("ContentControl");
}
这样就可以拿到对应的 xaml 的控件,虽然界面都在不断变化,但是这里拿到的控件是需要使用强转的方式,一旦找不到控件就给一个异常。
如果在 xaml 忘记写了一个控件,通过 GetTemplateChild 方法会返回 null 而不是抛异常,但是建议在这个方法下面判断拿到的如果是空,就抛出异常
protected override void OnApplyTemplate()
{
var foo = GetTemplateChild("不存在的控件");
if (foo == null)
{
throw new ArgumentException("使用的模板不包含");
}
}
我通过去拿一个不存在的控件,拿到的是空判断是空就抛出异常
如果此时运行了代码,在 OnApplyTemplate 添加断点,会发现这个函数无法进来,原因是 Board 控件的构造函数还忘记写下面的代码
public Board()
{
this.DefaultStyleKey = typeof(Board);
}
通过这个方法就可以拿到在 xaml 定义的控件,拿到了之后就可以在代码修改,如何修改请看下面
布局
如果已经写了 xaml 在代码拿到了 xaml 的控件,自定义控件还可以修改布局的方式
先在界面添加一些元素
public ContentControl Content { get; set; } private Grid _grid; protected override void OnApplyTemplate()
{
Content = (ContentControl) GetTemplateChild("ContentControl");
_grid = new Grid()
{
Children =
{
new TextBlock()
{
Text = "欢迎访问我博客 lindexi.github.io 里面有很多 UWP WPF 博客",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
}
};
Content.Content = _grid; base.OnApplyTemplate();
}
通过重写 MeasureOverride 方法可以拿到测量的值,在 UWP 的布局和 WPF 的一样,都是先进过测量再进行控制每个控件的坐标和大小。
测量是什么?在 UWP 的布局过程,这里提高了布局过程,还需要继续解释一下什么是布局过程。在 UWP 会将所有的控件按照控件所在的容器,作为视觉树,视觉树的意思很简单,我有一个 Grid 在里面放在两个 Grid 同时又在第一个 Grid 里面添加一个文本,这时的控件可以使用树这个数据结构表示。第一个节点是最上面的,也是最外层的 Grid 这个 Grid 有两个子节点,分别就是放在 Grid 里面的两个 Grid 而这里的两个 Grid 的第一个 Grid 里面也有一个节点就是文本。
在 UWP 通过 xaml 界面就可以知道控件的树结构,如果熟悉树这个结构就知道,可以使用递归的方式处理。也就是一个节点只处理这个节点的子节点,而不处理子节点的子节点,所以 UWP 的布局就依赖这个视觉树,通过布局子节点的方式,然子节点自己递归这个布局方法,布局子节点的子节点。
那么布局是什么?布局就是让子节点控件放在该放的地方,虽然定义了视觉树,知道了一个控件的里面包含了哪些控件,但是这个控件还没准备好里面的控件的坐标和大小。例如我有一个容器是 StackPanel 这个容器需要让里面的控件按照垂直或水平的方式布局,也就是在 StackPanel 垂直布局里面的控件,第二个控件的坐标的 Y 点是第一个控件的坐标的 Y 点加上控件的高度。假如第一个控件也是一个容器,那么如何知道这个容器的的高度是多少?因为容器的大小可以是容器里面的元素决定的,需要让这个容器先知道他里面的控件的大小才可以知道容器的大小。
这就是测量的过程,测量的过程就是让每个控件知道子节点的大小,从而计算出控件的大小,然后将控件的大小返回给上一层,让上一层可以知道子节点的大小。有了测量的过程,在进行 StackPenel 布局的时候,就可以在测量的过程知道了控件大小,从而在可以安排每个控件坐标。
这里自定义的控件也是这样,通过重写 MeasureOverride 可以修改计算自定义控件的大小的方法,从而报告给上一层一个特殊的值。
如我这里的控件是想要上一层给我多大的空间,我就要多大的空间,我可以通过重写 MeasureOverride 方法,返回参数
protected override Size MeasureOverride(Size availableSize)
{
base.MeasureOverride(availableSize);
return availableSize;
}
因为我这个控件里面有一些控件是需要在测量的过程重新给他一个值,我就可以这样写
protected override Size MeasureOverride(Size availableSize)
{
_grid.Height = availableSize.Height;
_grid.Width = availableSize.Width; base.MeasureOverride(availableSize);
return availableSize;
}
处理测量的方法可以重写,布局的方法也可以重写
通过重写 ArrangeOverride 的方法可以做到实际的布局,从测量的方法传入的参数也许不是最外层控件在布局的时候传入的大小,假如我有一个 StackPanel 他的高度 100 宽度也是 100 在测量的过程就会传入大小是 100x100 但是在布局的过程就依赖当前的控件是在 StackPanel 的第几个控件,减去前面控件用的地方在是这个控件可以用的。
本文的控件是不需要重新布局的方法,现在看起来的控件的代码请看下面
[TemplatePart(Name = "ContentControl", Type = typeof(ContentControl))]
public sealed class Board : Control
{
public Board()
{
this.DefaultStyleKey = typeof(Board);
} protected override Size MeasureOverride(Size availableSize)
{
_grid.Height = availableSize.Height;
_grid.Width = availableSize.Width; base.MeasureOverride(availableSize);
return availableSize;
} public ContentControl Content { get; set; } private Grid _grid; protected override void OnApplyTemplate()
{
Content = (ContentControl) GetTemplateChild("ContentControl");
_grid = new Grid()
{
Children =
{
new TextBlock()
{
Text = "欢迎访问我博客 lindexi.github.io 里面有很多 UWP WPF 博客",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
}
};
Content.Content = _grid; base.OnApplyTemplate();
}
}
在界面添加这个控件然后运行一下,可以看到界面居中显示了这个控件
2018-10-22-win10-uwp-自定义控件入门的更多相关文章
- win10 uwp MVVM入门
MVVM 是一个强大的架构,基本从 WPF 开始,wr(我说的就是微软)就提倡使用 MVVM.它可以将界面和后台分离,让开发人员可以不关心界面是怎样,全心投入到后台代码编写中. 然后在编写完后台代码后 ...
- win10 uwp 自定义控件 SplitViewItem
本文主要是因为汉堡菜单里面列出的菜单很多重复的图标和文字,我把它作为控件,因为是随便写,可能存在错误,如果发现了,请和我说或关掉浏览器,请不要发不良言论. 我们使用汉堡菜单,经常需要一个 需要一个图标 ...
- win10 uwp 自定义控件初始化
我遇到一个问题,我在 xaml 用了我的自定义控件,但是我给他设置了一个值,但是什么时候我才可以获得这个值? 本文告诉大家,从构造函数.loaded.Initialized 的调用过程. 用最简单的方 ...
- NOIP模拟赛-2018.10.22
模拟赛 今天第一节课是历史,当然是不可能上的,一来到机房发现今天高二考试... 老师说以后可能还要给高一考...那还不如现在跟着做好了,毕竟在学长学姐中垫底显得没那么丢人 这套题风格挺奇怪的...为什 ...
- 2018.10.22 bzoj1009: [HNOI2008]GT考试(kmp+矩阵快速幂优化dp)
传送门 f[i][j]f[i][j]f[i][j]表示从状态"匹配了前i位"转移到"匹配了前j位"的方案数. 这个东西单次是可以通过跳kmp的fail数组得到的 ...
- 2018.10.22 bzoj1742: Grazing on the Run 边跑边吃草(区间dp)
传送门 区间dp入门题. 可以想到当前吃掉的草一定是一个区间(因为经过的草一定会吃掉). 然后最后一定会停在左端点或者右端点. f[i][j][0/1]f[i][j][0/1]f[i][j][0/1] ...
- 2018.10.22 cogs2471. [EZOI 2016]源氏的数学课(线段树)
传送门 线段树入门操作. 直接把题目给的(r−i+1)∗a[i](r-i+1)*a[i](r−i+1)∗a[i]拆开变成(r+1)∗1∗a[i]−i∗a[i](r+1)*1*a[i]-i*a[i](r ...
- 2018.10.22 bzoj4380: [POI2015]Myjnie(区间dp)
传送门 区间dp好题. f[i][j][k]f[i][j][k]f[i][j][k]表示区间[i,j][i,j][i,j]最小值为kkk时的最大贡献. 然后可以枚举端点转移. 当时口胡到这儿就不会了. ...
- POI 2018.10.22
[POI2015]ODW 喵锟讲过.分块. N>=blo,那就暴力倍增往上跳.O(N/blo*logN) N<blo,预处理,f[i][j]表示,i往上跳,每次跳j步,到根节点为止,权值和 ...
- noip训练 2018.10.22~2018.10.23
day1 100+100+0=200 T1 稍微比划一下,发现其实就是缩点双,然后区间最小值的和 T2 发现答案为原lis|+1|-1 对每个点做从前最长上升序列以及从后最长下降序列, 想了半个小时怎 ...
随机推荐
- python ddt 实现数据驱动
ddt 是第三方模块,需安装, pip install ddt DDT包含类的装饰器ddt和两个方法装饰器data(直接输入测试数据) 通常情况下,data中的数据按照一个参数传递给测试用例,如果da ...
- jdbc框架-dbutils的简单使用
jdbc框架-dbutils的简单使用 dbutils:是apache组织的一个工具类,jdbc的框架,更方便我们使用 使用步骤: 1.导入jar包(commons-dbutils-1.4.jar) ...
- srand函数
srand函数是随机数发生器的初始化函数. 原型: void srand(unsigned seed); 用法:它需要提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的rand()函数会 ...
- Eclipse中提示 找不到类 javax.servlet.http.HttpServletResponse
问题如题, 解决方案如下: 复制tomcat的安装路径下\lib\servlet-api.jar 到WEB-INF/lib下即可.
- JDK 8 中包列表及介绍
了解了Java 8中所有包的作用,对Java 8有了一个整体的了解,另外也是提高了自身的阅读能力.本文列出了Java 8中所有的包,并且对每一个包的功能做了简要的说明,希望对你有所帮助. ------ ...
- Java SDUT-2562_相似三角形
相似三角形 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 给出两个三角形的三条边,判断是否相似. Input 多组数据 ...
- 二分查找 Day08
package com.sxt.arraytest2; /* * 二分查找 前提:有序 */ public class TestBinarySearch { public static void ma ...
- iOS开发 分享到QQ空间提示"分享失败 应用不存在"
本人遇到该问题的原因是配置SDK初始化时的APPID错误,可以参考下shareSDK的集成文档中的一段话: 可选:支持QQ所需的相关配置及代码 登录QQ互联(http://connect.qq.com ...
- cume_dist(),允许并列名次、复制名次自动空缺,取并列后较大名次,结果如22355778……
将score按ID分组排名:cume_dist() over(partition by id order by score desc)*sum(1) over(partition by id) 将sc ...
- @AGC037 - E@ Reversing and Concatenating
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个长度为 N 且只包含小写字母的字符串 S ,你可以执行 ...