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里面简单计算的。

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

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

相关文章

PROTEL

PROTEL是什么 Protel软件是由Altium公司&#xff08;原为Protel Technology公司&#xff09;开发的一款电子设计自动化&#xff08;EDA&#xff09;软件&#xff0c;主要用于电子电路设计和印制电路板&#xff08;PCB&#xff09;制作。 学习Protel 99 SE的大致过程 原理图文…

将仓库A中的部分提交迁移到仓库B中

结论&#xff1a; 使用git format-patchgit am即可实现 使用场景&#xff1a; 例如仓库A这里有5个提交记录&#xff0c;commitid1, commitid2, commitid3, commitid4&#xff0c;commitid5 仓库B想用仓库A中提交的代码&#xff0c;手动改比较慢&#xff0c;当改动较多的时候…

【linux进程信号(一)】信号的概念以及产生信号的方式

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 进程信号 1. 前言2. 信号的基…

java数据结构与算法刷题-----LeetCode501. 二叉搜索树中的众数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 解题思路 二叉搜索树&#xff0c;是有序的&#xff0c;而其中序遍历正好是…

keil的首次尝试,芯片为stm32F103C6T6

已经试了一下&#xff0c;吐槽这个软件的使用好麻烦啊 安装 然后先去安装对应的pack 这个鬼玩意里找对应的芯片&#xff0c;或者去官网上下载 我是在这里搜到芯片&#xff0c;再去官网下载一个驱动 https://www.keil.arm.com/packs/stm32f1xx_dfp-keil/boards/ 会有一个安装…

安装淘宝镜像cnpm报错

npm 安装淘宝镜像报错 npm install -g cnpm --registryhttps://registry.npm.taobao.org 安装报 The operation was rejected by your operating system. npm ERR! Its possible that the file was already in use (by a text editor or antivirus), npm ERR! or that you la…

Jenkins使用遇到的一些问题

一&#xff1a;插件依赖报错 比如遇到一堆插件报错&#xff0c;不是提示版本对不上&#xff0c;就是启用不了 这样直接把Jenkins升级就行了&#xff0c;比如我这个是命令行启动的&#xff0c;直接把他替换就好了 如果是遇到插件依赖报错&#xff0c;比如A插件异常 则点击这个插…

【自然语言处理三-自注意self attention】

自然语言处理三-自注意力 self attention 自注意力是什么&#xff1f;自注意力模型出现的原因是什么&#xff1f;词性标注问题解决方法1-扩展window&#xff0c;引用上下文解决方法2-运用seq2seq架构新问题来了&#xff1a;参数量增加、无法并行的顽疾 自注意力self attention模…

嵌入式学习第二十一天!(线程)

线程&#xff1a; 1. 基本概念&#xff1a; 线程&#xff1a;线程是一个轻量级的进程&#xff0c;位于进程空间内部&#xff0c;一个进程中可以创建多个线程 2. 线程创建&#xff1a; 线程独占栈空间&#xff0c;文本段、数据段和堆区与进程共享 3. 线程调度&#xff1a; 与进程…

攻防世界MISC-神秘的交易

题目&#xff1a;--宝贝儿约吗~ --约~老地方吗 --嗯呐~等你哦 --上次送你的那张会员卡&#xff0c;我是用我们的门牌号的密码&#xff0c;爱你 --晚上等我&#xff01; 惊了&#xff01;怪不得柠檬师傅最近总往北街跑。看他午休的时候&#xff0c;我悄悄拿走了他的会员卡&#…

变大再变大,matplotlib坐标轴刻度设置

文章目录 对数坐标系自定义映射对数图表重置刻度 matplotlib教程&#xff1a;初步&#x1f4c8;子图绘制&#x1f4c8;坐标投影 matplotlib支持重设坐标轴刻度&#xff0c;包括刻度值的重新映射和刻度标签的重新映射。前者可以调整坐标刻度的缩放比例&#xff0c;后者可以更改…

基于springboot实现的海鲜销售系统

一、系统架构 前端&#xff1a;html | bootstrap | vue | js | css 后端&#xff1a;springboot | springdata-jpa 环境&#xff1a;jdk1.8 | mysql | maven | redis 二、代码及数据库 三、功能介绍 01. web端-注册 02. web端-登录 03. web端-首页 04. web端-…

Gateway网关实战

目录 什么是API网关&#xff1f; 网关为什么出现&#xff1f; 什么是Spring Cloud Gateway&#xff1f; 核心概念 Spring Cloud Gateway实战 路由断言工厂&#xff08;Route Predicate Factories&#xff09;配置 路径匹配 Header匹配 过滤器工厂&#xff08; Gateway…

【kubernetes】二进制部署k8s集群之cni网络插件flannel和calico工作原理

k8s集群的三种接口 k8s集群有三大接口&#xff1a; CRI&#xff1a;容器进行时接口&#xff0c;连接容器引擎--docker、containerd、cri-o、podman CNI&#xff1a;容器网络接口&#xff0c;用于连接网络插件如&#xff1a;flannel、calico、cilium CSI&#xff1a;容器存储…

NVIDIA\CUDA\cudnn安装以及visual studio2022编译安装ceres2.2.0库

一、NVIDIA驱动安装 网址:官方驱动 | NVIDIA 因为本文之后需要visual studio2022进行编译&#xff0c;所以在安装NVIDIA\CUDA\cudnn之前你先得安装visual studio2022 点击NVIDIA控制面板&#xff0c;NVIDIA Control Panel 查看产品家族 根据产品家族选择驱动&#xff0c;点…

[算法沉淀记录] 排序算法 —— 选择排序

排序算法 —— 选择排序 基本概念 选择排序是一种简单的排序算法&#xff0c;它的工作原理是每次从待排序的列表中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;将其与列表中的第一个位置交换&#xff0c;然后继续对剩余的元素进行排序&#xff0c;直到整个列表…

基于Java SSM框架实现家庭食谱管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现家庭食谱管理系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个家庭食谱管理系统 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论…

省内顺丰寄一台电脑多少钱,顺丰不会乱丢包裹

省内用顺丰快递寄电脑要多少钱&#xff1f; 使用顺丰速运。 顺丰快递不会乱扔包裹。 根据地区不同&#xff0c;邮费预计在120至150元左右。 有些地方顺丰不允许寄电脑&#xff0c;因为电脑特别容易损坏。 一般来说&#xff0c;您需要自己做。 有的顺丰还帮忙在电脑主机的外箱上…

喜报|迪捷软件入选工信部“2023年信息技术应用创新解决方案”

为进一步推进信创生态建设&#xff0c;激发产业自主创新活力&#xff0c;高效促进供需协同发展&#xff0c;加强区域联动和资源整合&#xff0c;国家工业和信息化部网络安全产业发展中心&#xff08;工业和信息化部信息中心&#xff09;联合相关单位&#xff0c;遴选了一批可复…

2024年江苏事业单位招聘报名指南

江苏事业单位目前已出的公告中&#xff0c;扬州和常州的报名时间相对较早&#xff0c;2月27日就开始报名了&#xff1b;其他大多在2月28日或3月1日起开始报名。 报名请移步<江苏人事考试网> 【报名时间】 2月28日9:00-3月4日16:00#图文万粉激励计划# 【资格初审】2月28…