【WPF.NET开发】创建模板

本文内容

  1. 何时创建 ControlTemplate
  2. 先决条件
  3. 创建 ControlTemplate
  4. 使用模板
  5. 添加触发器
  6. 使用 VisualState

使用 Windows Presentation Foundation (WPF),可以使用自己的可重用模板自定义现有控件的可视结构和行为。 可以对应用程序、窗口和页面全局应用模板,也可以将模板直接应用于控件。 需要新建控件的大多数场景均可改为为现有控件创建新模板。

本文将介绍如何为 Button 控件创建新的 ControlTemplate。

1、何时创建 ControlTemplate

控件有许多属性,例如 Background、Foreground 和 FontFamily。 这些属性控制控件外观的不同方面,但可通过设置这些属性进行的更改有限。 例如,可以从 CheckBox 中将 Foreground 属性设置为蓝色,并将 FontStyle 设置为斜体。 要自定义设置控件中其他属性无法实现的控件外观时,则创建 ControlTemplate。

在多数用户界面中,按钮的总体外观相同:即一个包含某些文本的矩形。 若想要创建一个圆形的按钮,可以创建一个继承自该按钮或重新创建该按钮功能的新控件。 此外,新用户控件还会提供圆形视觉对象。

通过自定义现有控件的可视布局,可以避免创建新控件。 借助圆形按钮,可创建具有所需可视布局的 ControlTemplate。

另一方面,如果你需要具有新功能、其他属性和新设置的控件,可创建新的 UserControl。

2、先决条件

创建新的 WPF 应用程序,在 MainWindow.xaml(或选择的其他窗口)的 <Window> 元素中设置以下属性:

展开表

属性Value
TitleTemplate Intro Sample
SizeToContentWidthAndHeight
MinWidth250

将 <Window> 元素的内容设置为以下 XAML:

<StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button>Button 2</Button>
</StackPanel>

最后,MainWindow.xaml 文件应如下所示:

<Window x:Class="IntroToStylingAndTemplating.Window1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:IntroToStylingAndTemplating"mc:Ignorable="d"Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250"><StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button>Button 2</Button></StackPanel>
</Window>

如果你运行应用程序,它将如下所示:

unstyled-button.png?view=netdesktop-8.0

3、创建 ControlTemplate

声明 ControlTemplate 的最常见方法是在 XAML 文件的 Resources 部分中声明为资源。 模板是资源,因此它们遵从适用于所有资源的相同范围规则。 简言之,声明模板的位置会影响模板的应用范围。 例如,如果在应用程序定义 XAML 文件的根元素中声明模板,则该模板可以在应用程序中的任何位置使用。 如果在窗口中定义模板,则仅该窗口中的控件可以使用该模板。

首先,将 Window.Resources 元素添加到 MainWindow.xaml 文件:

<Window x:Class="IntroToStylingAndTemplating.Window2"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:IntroToStylingAndTemplating"mc:Ignorable="d"Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250"><Window.Resources></Window.Resources><StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button>Button 2</Button></StackPanel>
</Window>

使用以下属性集创建新的 <ControlTemplate>:

展开表

属性Value
x:Keyroundbutton
TargetTypeButton

此控制模板很简单:

  • 控件的根元素 Grid
  • 用于绘制按钮圆形外观的 Ellipse
  • 用于显示用户指定的按钮内容的 ContentPresenter
<ControlTemplate x:Key="roundbutton" TargetType="Button"><Grid><Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" /><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid>
</ControlTemplate>

TemplateBinding

创建新的 ControlTemplate 时,可能仍然想要使用公共属性更改控件外观。 TemplateBinding 标记扩展将 ControlTemplate 中元素的属性绑定到由控件定义的公共属性。 使用 TemplateBinding 时,可让控件属性用作模板参数。 换言之,设置控件属性后,该值将传递到包含 TemplateBinding 的元素。

椭圆形

请注意,<Ellipse> 元素的 Fill 和 Stroke 属性绑定到了控件的 Foreground 和 Background 属性。

ContentPresenter

此外,还将 <ContentPresenter> 元素添加到了模板。 此模板专为按钮设计,因此请注意该按钮继承自 ContentControl。 此按钮会显示该元素的内容。 可以在该按钮中设置任何内容,例如纯文本,甚至其他控件。 以下两个按钮均有效:

<Button>My Text</Button><!-- and --><Button><CheckBox>Checkbox in a button</CheckBox>
</Button>

在前面的两个示例中,将文本和复选框设置为 Button.Content 属性。 设置为内容的任何内容都可通过 <ContentPresenter> 显示,这是模板的功能。

若将 ControlTemplate 应用到 ContentControl 类型(例如 Button),将在元素树中搜索 ContentPresenter。 若找到了 ContentPresenter,模板会自动将控件的 Content 属性绑定到 ContentPresenter

4、使用模板

找到本文开头声明的按钮。

<StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button>Button 2</Button>
</StackPanel>

将第二个按钮的 Template 属性设置为 roundbutton 资源:

<StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

若运行项目并查看结果,将看到此按钮具有圆形背景。

styled-button.png?view=netdesktop-8.0

你可能已注意到,此按钮不是一个圆形,而是倾斜的。 由于 <Ellipse> 元素的工作方式,它始终会扩展并填充可用空间。 将此按钮的 width 和 height 属性更改为同一个值,以使圆形均衡:

<StackPanel Margin="10"><Label>Unstyled Button</Label><Button>Button 1</Button><Label>Rounded Button</Label><Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
</StackPanel>

styled-uniform-button.png?view=netdesktop-8.0

5、添加触发器

即使已应用模板的按钮看上去与众不同,但它的行为与任何其他按钮相同。 若按下此按钮,将触发 Click 事件。 不过,你可能已注意到,当你将鼠标移到此按钮上方时,此按钮的视觉对象不会改变。 这些视觉对象交互均由模板定义。

通过 WPF 提供的动态事件和属性系统,你可以监视特定属性是否是某个值,必要时还可重新设置模板样式。 在此示例中,你将监视按钮的 IsMouseOver 属性。 当鼠标位于控件上方时,使用新颜色设置 <Ellipse> 的样式。 此触发器类型称为 PropertyTrigger。

必须为 <Ellipse> 添加一个可引用的名称,以便于触发器起作用。 将其命名为“backgroundElement”。

<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />

接下来,将新的 Trigger 添加到 ControlTemplate.Triggers 集合。 此触发器将监视 IsMouseOver 事件是否为值 true

<ControlTemplate x:Key="roundbutton" TargetType="Button"><Grid><Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" /><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="true"></Trigger></ControlTemplate.Triggers>
</ControlTemplate>

接下来,将 <Setter> 添加到 <Trigger>,后者会将 <Ellipse> 的 Fill 属性更改为一种新颜色。

<Trigger Property="IsMouseOver" Value="true"><Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>

运行该项目。 请注意,当你将鼠标移到按钮上方时,<Ellipse> 的颜色会改变。

mouse-move-over-button.gif?view=netdesktop-8.0

6、使用 VisualState

视觉状态由控件定义和触发。 例如,当鼠标移到控件上方时,将触发 CommonStates.MouseOver 状态。 可以基于控件的当前状态对属性更改进行动画处理。 在上一部分中,当 IsMouseOver 属性为 true 时,使用 <PropertyTrigger> 将按钮的背景更改为 AliceBlue。 可改为创建一个视觉状态,来对此颜色的更改进行动画处理,以实现平稳过过渡。 

若要将 <PropertyTrigger> 转换为动画效果的可视状态,首先要从模板删除 <ControlTemplate.Triggers> 元素。

<ControlTemplate x:Key="roundbutton" TargetType="Button"><Grid><Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" /><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid>
</ControlTemplate>

接下来,在控件模板的 <Grid> 根中,添加 <VisualStateManager.VisualStateGroups>,其中包含 CommonStates 的 <VisualStateGroup>。 定义两种状态:Normal 和 MouseOver

<ControlTemplate x:Key="roundbutton" TargetType="Button"><Grid><VisualStateManager.VisualStateGroups><VisualStateGroup Name="CommonStates"><VisualState Name="Normal"></VisualState><VisualState Name="MouseOver"></VisualState></VisualStateGroup></VisualStateManager.VisualStateGroups><Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" /><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid>
</ControlTemplate>

触发 <VisualState> 时,将应用该状态中定义的任何动画。 为每种状态创建动画。 动画位于 <Storyboard> 元素中。 

此状态对椭圆填充进行动画处理,将其还原为控件的 Background 颜色。

  • 此状态对椭圆 Background 颜色进行动画处理,将其更改为新颜色 Yellow

    <Storyboard><ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:0.3"/>
    </Storyboard>
    

现在,<ControlTemplate> 应如下所示。

<ControlTemplate x:Key="roundbutton" TargetType="Button"><Grid><VisualStateManager.VisualStateGroups><VisualStateGroup Name="CommonStates"><VisualState Name="Normal"><Storyboard><ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"To="{TemplateBinding Background}"Duration="0:0:0.3"/></Storyboard></VisualState><VisualState Name="MouseOver"><Storyboard><ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:0.3"/></Storyboard></VisualState></VisualStateGroup></VisualStateManager.VisualStateGroups><Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" /><ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid>
</ControlTemplate>

运行该项目。 请注意,当你将鼠标移到按钮上方时,<Ellipse> 的颜色会进行动画处理。

mouse-move-over-button-visualstate.gif?view=netdesktop-8.0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295026.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库学习日常案例20231221-oracle libray cache lock分析

1 问题概述&#xff1a; 阻塞的源头为两个ddl操作导致大量的libray cache lock 其中1133为gis sde的create table as语句。 其中697为alter index语句。

国图公考:公考常识题如何复习更高效?

公务员考试是许多人求职的首选之一&#xff0c;而公考常识题作为公务员考试的重要组成部分&#xff0c;对于考生来说具有很高的分值。在这一模块的复习中&#xff0c;有以下几点可以帮助考生提高学习效率&#xff0c;一起来看一下吧! 一、明确复习目标 了解自己在各个知识点上…

arkTS 基础知识

entry 主模块 Harmony工程模块 编译构建生成一个Hap包 程序主要入口 --src --main --ets:用于存放ets源码 --entryability&#xff1a;EntryAbility.ts 应用/服务的入口 --pages:Index.ets …

Liteos移植_STM32_HAL库

0 开发环境 STM32CubeMX(HAL库)keil 5正点原子探索者STM32F4ZET6LiteOS-develop分支 1 STM32CubeMX创建工程 如果有自己的工程&#xff0c;直接从LiteOS源码获取开始 关于STM32CubeMX的安装&#xff0c;看我另一篇博客STM32CubeMX安装 工程配置 创建新工程 选择芯片【STM32F…

SwiftUI 趣谈之:绝不可能(Never)的 View!

概览 SwiftUI 的出现极大的解放了秃头码农们的生产力。SwiftUI 中众多原生和自定义视图对于我们创建精彩撩人的 App 功不可没&#xff01; 不过&#xff0c;倘若小伙伴们略微留意过 SwiftUI 框架头文件里的源代码&#xff0c;就会发现里面嵌有一些奇怪 Never 类型&#xff0c…

一文掌握分布式锁:Mysql/Redis/Zookeeper实现

目录 一、项目准备spring项目数据库 二、传统锁演示超卖现象使用JVM锁解决超卖解决方案JVM失效场景 使用一个SQL解决超卖使用mysql悲观锁解决超卖使用mysql乐观锁解决超卖四种锁比较Redis乐观锁集成Redis超卖现象redis乐观锁解决超卖 三、分布式锁概述四、Redis分布式锁实现方案…

PyQt6 利用Pyinstaller打包发布程序

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计53条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

物联网产品设计,聊聊设备OTA的升级

物联网产品设计部分的OTA设备固件是一个非常重要的部分&#xff0c;能够实现升级用户服务、保障系统安全等功能。 在迅速变化和发展的物联网市场&#xff0c;新的产品需求不断涌现&#xff0c;因此对于智能硬件设备的更新需求就变得空前高涨&#xff0c;设备不再像传统设备一样…

数据分析基础之《numpy(5)—合并与分割》

了解即可&#xff0c;用panads 一、作用 实现数据的切分和合并&#xff0c;将数据进行切分合并处理 二、合并 1、numpy.hstack 水平拼接 # hstack 水平拼接 a np.array((1,2,3)) b np.array((2,3,4)) np.hstack((a, b))a np.array([[1], [2], [3]]) b np.array([[2], […

20231222给NanoPC-T4(RK3399)开发板的适配原厂Android10的挖掘机方案并跑通AP6398SV

20231222给NanoPC-T4(RK3399)开发板的适配原厂Android10的挖掘机方案并跑通AP6398SV 1、简略步骤&#xff1a;rootrootrootroot-X99-Turbo:~/3TB/3399-android10$ cat Rockchip_Android10.0_SDK_Release.tar.gz0* > Rockchip_Android10.0_SDK_Release.tar.gz rootrootrootro…

Spring Boot3通过GraalVM生成exe执行文件

一、安装GraalVM 1、官网&#xff1a;https://www.graalvm.org/downloads/ 2、配置环境变量 2.1、环境变量必须使用JAVA_HOME&#xff0c;否则会出现问题 2.2、在系统变量配置Path,%JAVA_HOME%\bin&#xff0c;注意必须放在顶部第一位 2.3、配置jdk的环境变量&#xff0c;在P…