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也在积极探索虚拟社交的未来,将其视为元宇宙时…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
 
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
 
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
 
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
 
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
 
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
 
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
