WPF 附加属性+控件模板,完成自定义控件。建议观看HandyControl源码
文章目录
- 相关连接
- 前言
- 需要实现的效果
- 附加属性
- 添加附加属性,以Test修改FontSize为例
- 依赖属性使用
- 触发器使用
- 直接操控
- 结论
- 控件模板,在HandyControl的基础上面进行修改
- 参考HandyControl的源码
- 控件模板原型
- 控件模板
- 控件模板触发器
- 完整样式
- 简单使用
- 结论
相关连接
WPF控件模板(6)
WPF 附加属性
WPF教程:附加属性
前言
今天说服了领导用WPF开发前端,原因就是开发相对来说比较方便,写小项目就不用前后端分离什么的了。反正就是有个机会写WPF了,真开心。我已经写了一年的Uniapp了
需要实现的效果
就是想写一个简单的变色控件。
附加属性
如果想知道附加属性,就得先了解依赖属性。详细的可以看我这篇文章
WPF 用户控件依赖属性赋值
添加附加属性,以Test修改FontSize为例
知道了依赖属性之后,我解释一下附加属性是什么意思。附加属性就是为了方便在原有的控件基础上面进行细微的修改。我们先保证编译通过
附加属性的快捷键是propa
简单给TextBox添加一个附加属性
public partial class TextBlockExtension{public static int GetTest(DependencyObject obj){return (int)obj.GetValue(TestProperty);}public static void SetTest(DependencyObject obj, int value){obj.SetValue(TestProperty, value);}// Using a DependencyProperty as the backing store for Test. This enables animation, styling, binding, etc...public static readonly DependencyProperty TestProperty =DependencyProperty.RegisterAttached("Test", typeof(int), typeof(TextBox), new PropertyMetadata(10));}
这样我们就能编译通过了。
<TextBlock Text="用户" wpfEx:TextBlockExtension.Test="2"/>
依赖属性使用
依赖属性有两种使用方法
触发器使用
样式定义
<!--一个简单的FontSize修改--><Style x:Key="UserSelection"TargetType="TextBlock"><!--因为Triggers只有等于判断,所以这里简单写了一下--><Style.Triggers><Trigger Property="wpfEx:TextBlockExtension.Test"Value="10"><Setter Property="FontSize"Value="10" /></Trigger><Trigger Property="wpfEx:TextBlockExtension.Test"Value="20"><Setter Property="FontSize"Value="20" /></Trigger></Style.Triggers></Style>
简单使用
<TextBlock Text="用户"wpfEx:TextBlockExtension.Test="10" Style="{StaticResource UserSelection}"></TextBlock><TextBlock Text="用户"wpfEx:TextBlockExtension.Test="20"Style="{StaticResource UserSelection}"></TextBlock>
直接操控
附加属性修改
//如果想直接操控元素,得在PropertyMetadata进行操控。记得设置初始值
public static readonly DependencyProperty TestProperty =DependencyProperty.RegisterAttached("Test", typeof(int), typeof(TextBox), new PropertyMetadata(10,(s, e) =>{//s是控件本身,var mdp = s as TextBlock;//如果控件是该元素的父组件,类似于Grid和DockPanel,就使用Parent来寻找,这里不展开//var mdpParent = (s as FrameworkElement).Parent as TextBlock;if (mdp != null && e.NewValue != null){mdp.FontSize = (int)e.NewValue;}}));
<!--如果想要预览生效需要重新编译一下-->
<TextBlock Text="用户"wpfEx:TextBlockExtension.Test="15"></TextBlock>
<TextBlock Text="用户"wpfEx:TextBlockExtension.Test="20">
</TextBlock>
<!--因为我们设置了默认值为10,所以这里是10-->
<TextBlock Text="用户">
</TextBlock>
<!--优先级还是依赖属性高,所以这里是30而不是默认值10-->
<TextBlock Text="用户" FontSize="30">
</TextBlock>
结论
附加属性和依赖属性差不多,就是声明麻烦一点。因为Get,Set是需要额外写的。
控件模板,在HandyControl的基础上面进行修改
控件模板一般用于按钮,我们只要会按钮的控件模板就可以了。
WPF控件模板(6)
参考HandyControl的源码
HandyControl 页面
HandyControl的Button有IconButton的样式源码。看一下还是挺有收获的。
参考样式代码
<Style x:Key="ButtonDashedBaseStyle" BasedOn="{StaticResource ButtonBaseStyle}" TargetType="Button"><Setter Property="Background" Value="Transparent"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><hc:DashedBorder BorderDashArray="3,2" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="Transparent" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"><StackPanel Orientation="Horizontal" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}"><Path x:Name="PathMain" Width="{TemplateBinding hc:IconElement.Width}" Height="{TemplateBinding hc:IconElement.Height}" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" Stretch="Uniform" Data="{TemplateBinding hc:IconElement.Geometry}"/><ContentPresenter x:Name="ContentPresenterMain" RecognizesAccessKey="True" VerticalAlignment="Center" Margin="6,0,0,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/></StackPanel></hc:DashedBorder><ControlTemplate.Triggers><Trigger Property="Content" Value="{x:Null}"><Setter Property="Visibility" Value="Collapsed" TargetName="ContentPresenterMain"/></Trigger><Trigger Property="hc:IconElement.Geometry" Value="{x:Null}"><Setter Property="Visibility" Value="Collapsed" TargetName="PathMain"/><Setter Property="Margin" Value="0" TargetName="ContentPresenterMain"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>
控件模板原型
我们想写一个控件模板,如果不是很熟练,我们就先把控件模板的原型写出来,这样更利于理解。
<DockPanel><!--这里仿照HandyControl,使用Gemotery。IconPacks怎么转Gemotery可以看我的文章--><Border DockPanel.Dock="Top"Width="50"Height="50"CornerRadius="25"Background="DeepSkyBlue"><Path Data="{wpfEx:MaterialGeometry Kind=BellRing}"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"SnapsToDevicePixels="True"Stretch="Uniform"Width="25"Height="25"Fill="White" /></Border><TextBlock Text="TIM登录"HorizontalAlignment="Center" /></DockPanel>
控件模板
<Style x:Key="UserSelection"TargetType="RadioButton"BasedOn="{StaticResource {x:Type RadioButton}}"><Setter Property="Template"><Setter.Value><!--先按照之前的样式粘贴一下--><ControlTemplate TargetType="RadioButton"><DockPanel><Border DockPanel.Dock="Top"Width="50"Height="50"CornerRadius="25"Background="DeepSkyBlue"><Path Data="{wpfEx:MaterialGeometry Kind=BellRing}"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"SnapsToDevicePixels="True"Stretch="Uniform"Width="25"Height="25"Fill="White" /></Border><ContentPresenter x:Name="ContentPresenterMain"RecognizesAccessKey="True"VerticalAlignment="Center"Margin="6,0,0,0"SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /></DockPanel></ControlTemplate></Setter.Value></Setter>
</Style>
然后里面能绑定的就绑定。也是照着HandyControl改的。注意这里的Banding用的是TemplateBinding
修改好的效果
<!--一个简单的FontSize修改-->
<Style x:Key="UserSelection"TargetType="RadioButton"BasedOn="{StaticResource {x:Type RadioButton}}"><Setter Property="Template"><Setter.Value><!--先按照之前的样式粘贴一下--><ControlTemplate TargetType="RadioButton"><DockPanel><Border DockPanel.Dock="Top"Width="{TemplateBinding hc:IconElement.Width}"Height="{TemplateBinding hc:IconElement.Height}"CornerRadius="25"Background="{TemplateBinding Foreground}"><Path Data="{TemplateBinding hc:IconElement.Geometry}"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"SnapsToDevicePixels="True"Stretch="Uniform"Width="25"Height="25"Fill="{TemplateBinding Background}" /></Border><ContentPresenter x:Name="ContentPresenterMain"RecognizesAccessKey="True"VerticalAlignment="Center"Margin="6,0,0,0"SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /></DockPanel></ControlTemplate></Setter.Value></Setter>
</Style>
简单使用
<RadioButton Content="TIM登录"GroupName="UserSelect"Style="{StaticResource UserSelection}"Foreground="DeepSkyBlue"Background="White"hc:IconElement.Geometry="{wpfEx:MaterialGeometry Kind=AbTesting}" />
控件模板触发器
完整样式
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:wpfEx="clr-namespace:BluetoothWPF.WpfExtensions"xmlns:hc="https://handyorg.github.io/handycontrol"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><!--一个简单的FontSize修改--><Style x:Key="UserSelection"TargetType="RadioButton"BasedOn="{StaticResource {x:Type RadioButton}}"><Setter Property="Foreground"Value="Gray" /><Setter Property="Template"><Setter.Value><!--先按照之前的样式粘贴一下--><ControlTemplate TargetType="RadioButton"><DockPanel><Border DockPanel.Dock="Top"Width="70"Height="70"CornerRadius="35"x:Name="Background"><Path Data="{TemplateBinding hc:IconElement.Geometry}"x:Name="Icon"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"SnapsToDevicePixels="True"Stretch="Uniform"Width="35"Height="35"Fill="Gray" /></Border><ContentPresenter x:Name="ContentPresenterMain"RecognizesAccessKey="True"VerticalAlignment="Center"HorizontalAlignment="Center"Margin="6,0,0,0"SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /></DockPanel><ControlTemplate.Triggers><Trigger Property="IsMouseOver"Value="True"><Setter TargetName="Background"Property="Background"Value="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}" /><Setter TargetName="Icon"Property="Fill"Value="White" /></Trigger><Trigger Property="IsFocused"Value="True"><Setter TargetName="Background"Property="Background"Value="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}" /><Setter TargetName="Icon"Property="Fill"Value="White" /></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="RadioButton"x:Key="UserSelectioin_Admin"BasedOn="{StaticResource UserSelection}"><Setter Property="HorizontalAlignment"Value="Right" /><Setter Property="Margin"Value="0 0 10 0" /><Setter Property="Background"Value="DeepSkyBlue" /><Setter Property="hc:IconElement.Geometry"Value="{wpfEx:MaterialGeometry Kind=AccountLock}" /><Setter Property="Content"Value="管理员登录" /></Style><Style TargetType="RadioButton"x:Key="UserSelectioin_User"BasedOn="{StaticResource UserSelection}"><Setter Property="HorizontalAlignment"Value="Left" /><Setter Property="Margin"Value="10 0 0 0" /><Setter Property="Background"Value="Green" /><Setter Property="hc:IconElement.Geometry"Value="{wpfEx:MaterialGeometry Kind=Account}" /><Setter Property="Content"Value="用户" /></Style>
</ResourceDictionary>
简单使用
<RadioButton Style="{StaticResource UserSelectioin_Admin}" />
<RadioButton Style="{StaticResource UserSelectioin_User}" />
结论
HandyControl的源码看了真的是打开眼界,但是WPF的Xaml有一个无法在内部简单计算的问题。比如我想Witdh=Height = CornerRadius*2。我可能就要写个触发器了。我后面回去测试一下有没有方法可以在Xaml里面简单计算的。
相关文章:

WPF 附加属性+控件模板,完成自定义控件。建议观看HandyControl源码
文章目录 相关连接前言需要实现的效果附加属性添加附加属性,以Test修改FontSize为例依赖属性使用触发器使用直接操控 结论 控件模板,在HandyControl的基础上面进行修改参考HandyControl的源码控件模板原型控件模板 控件模板触发器完整样式简单使用 结论 …...
编程笔记 Golang基础 040 defer、panic 和 recover
编程笔记 Golang基础 040 defer、panic 和 recover 一、defer二、panic三、recover小结 在Go语言中,defer、panic 和 recover 是一组用于错误处理和控制程序流程的关键字。它们之间的交互有助于实现异常处理机制,并确保资源的正确释放。 一、defer defe…...
通过redfish协议实现服务器固件升级、从虚拟光驱启动自检盘并等待完成,最后截图保存
通过redfish协议实现服务器固件升级、从虚拟光驱启动自检盘并等待完成,最后截图保存 版本信息代码新开发的PCIE设备在做服务器适配时,有时需要服务器厂家更新BMC或BIOS固件。同时,我们也希望对PCIE设备做一些检测,最后收集一些信息存档。如果需要处理的服务器很多,通过BMC的界面…...

ARM 版银河麒麟桌面系统下 Qt 开发环境搭建指南
目录 前言安装Linux ARM 版 QtCreator配置 Qt Creator配置构建套件 第一个麒麟 Qt 应用程序小结 前言 在上一篇文章信创ARM架构QT应用开发环境搭建中建议大家使用 Ubuntu X86 系统作为信创 ARM 架构 QT 应用的开发环境,里面使用了交叉编译的方式。这对于自己的 Qt …...
架构面试题汇总:缓存(二)
目录 1. 问题:什么是缓存,以及为什么我们需要缓存?2. 问题:你能解释一下缓存击穿、缓存雪崩和缓存预热是什么吗?3. 问题:如何在Java中实现缓存?4. 问题:你如何决定哪些数据应该被缓存…...
【docker入门】1-
文章目录 参考: Docker – 容器虚拟化平台。 参考: docker入门,这一篇就够了。【零基础入门Docker】Dockerfile中的USER指令以及dockerfile命令详解dockerfile copy命令...

微信小程序-全局配置
个人笔记,仅供参考。 1.entryPagePath 代码: "entryPagePath": "pages/index/index" 具体用法: 2.pages 小程序中新增/减少页面,都需要对 pages 数组进行修改。 代码: "pages": [&…...
【Android】性能优化之内存、网络、布局、卡顿、安装包、启动速度优化
欢迎来到 Android 开发老生常谈的性能优化篇,本文将性能优化划分为内存、网络、布局、卡顿、安装包、启动速度七块,从这七块优化出发,阐述优化的 Application 的方式。 目录 内存优化避免内存泄漏使用内存分析工具优化数据结构和算法数据缓存…...

第3.6章:StarRocks数据导入——DataX StarRocksWriter
一、Datax 1.1 DataX 3.0概述 DataX3.0是一个异构数据源离线同步工具,可以方便的对各种异构数据源进行高效的数据同步。 其github地址为: https://github.com/alibaba/DataX/blob/master/introduction.mdhttps://github.com/alibaba/DataX/blob/mast…...

【非递归版】归并排序算法(2)
目录 MergeSortNonR归并排序 非递归&归并排序VS快速排序 整体思想 图解分析 代码实现 时间复杂度 归并排序在硬盘上的应用(外排序) MergeSortNonR归并排序 前面的快速排序的非递归实现,我们借助栈实现。这里我们能否也借助栈去…...
[C++]C++实现本地TCP通讯的示例代码
这篇文章主要为大家详细介绍了C如何利用TCP技术,实现本地ROS1和ROS2的通讯,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下 概要服务端代码 头文件源代码客户端代码 概要 利用TCP技术,实现本地ROS1和ROS2的通讯。 服务端代码 头文件 #include &…...

Sora - 探索AI视频模型的无限可能
文章目录 每日一句正能量前言技术解析应用场景未来展望伦理与创意用户体验与互动后记 每日一句正能量 . 一个人,如果没有经受过投资失败的痛楚,又怎么会看到绝望之后的海阔天空。很多时候,经历了人生中最艰难的事,反而锻造了最坚强…...

【JavaScript 漫游】【022】事件模型
文章简介 本篇文章为【JavaScript 漫游】专栏的第 022 篇文章,对 JavaScript 中事件模型相关的知识点进行了总结。 监听函数 浏览器的事件模型,就是通过监听函数(listener)对事件做出反应。事件发生后,浏览器监听到…...

【加密算法】RSA非对称加密算法简介
目录 前言 工作原理 密钥生成 加密和解密 在Java中使用RSA 生成密钥对 加密和解密数据 加密数据 解密数据 注意事项和最佳实践 结论 前言 RSA(Rivest-Shamir-Adleman)是一种基于数论的非对称加密算法,广泛应用于数字签名、数据加密…...

深入理解 JavaScript 对象原型,解密原型链之谜(上)
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

产品经理学习-产品运营《什么是SOP》
目录 什么是SOP 如何执行SOP 执行SOP的重点 什么是SOP SOP就是项目流程操作的说明书 日常工作中的例行操作: 例行操作是指,在每一天,针对每一个用户,在每个项目之中,都必须完成的操作,这些必须完成的操…...
大数据Hadoop生态圈
存储: HDFS(namenode,datanode) 计算:MapReduce(mapreduce,基于磁盘) 便于用sql操作:Hive(核心 metastore,存储这些结构化的数据),同类的还有Impala,hbase等 基于yaml的资源调度 hive &…...
算法简介:查找与算法运行时间
文章目录 1. 二分查找与简单查找1.1 运行时间 2. 旅行商问题 算法是一组完成任务的指令。任何代码片段都可以视为算法。 1. 二分查找与简单查找 二分查找是一种算法,其输入是一个有序的元素列表,如果要查找的元素包含在列表中,二分查找返回…...

零基础C++开发上位机--基于QT5.15的串口助手(三)
本系列教程本着实践的目的,争取每一节课都带大家做一个小项目,让大家多实践多试验,这样才能知道自己学会与否。 接下来我们这节课,主要学习一下QT的串口编程。做一款自己的串口助手,那么这里默认大家都是具备串口通信…...

Facebook的虚拟社交愿景:元宇宙时代的新起点
在当今数字化时代,社交媒体已经成为人们生活中不可或缺的一部分。而随着科技的不断进步和社会的发展,元宇宙已经成为了人们关注的热点话题之一。作为社交媒体的领军企业之一,Facebook也在积极探索虚拟社交的未来,将其视为元宇宙时…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...