当前位置: 首页 > news >正文

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.lryc.cn/news/306774.html

相关文章:

  • 编程笔记 Golang基础 040 defer、panic 和 recover
  • 通过redfish协议实现服务器固件升级、从虚拟光驱启动自检盘并等待完成,最后截图保存
  • ARM 版银河麒麟桌面系统下 Qt 开发环境搭建指南
  • 架构面试题汇总:缓存(二)
  • 【docker入门】1-
  • 微信小程序-全局配置
  • 【Android】性能优化之内存、网络、布局、卡顿、安装包、启动速度优化
  • 第3.6章:StarRocks数据导入——DataX StarRocksWriter
  • 【非递归版】归并排序算法(2)
  • [C++]C++实现本地TCP通讯的示例代码
  • Sora - 探索AI视频模型的无限可能
  • 【JavaScript 漫游】【022】事件模型
  • 【加密算法】RSA非对称加密算法简介
  • 深入理解 JavaScript 对象原型,解密原型链之谜(上)
  • 产品经理学习-产品运营《什么是SOP》
  • 大数据Hadoop生态圈
  • 算法简介:查找与算法运行时间
  • 零基础C++开发上位机--基于QT5.15的串口助手(三)
  • Facebook的虚拟社交愿景:元宇宙时代的新起点
  • 【深度学习笔记】4_6 模型的GPU计算
  • 留学申请过程中如何合理使用AI?大学招生官怎么看?
  • vue2与vue3的diff算法有什么区别
  • ES小总结
  • vue2与vue3中父子组件传参的区别
  • 使用vuetify实现全局v-alert消息通知
  • CentOS 7.9上编译wireshark 3.6
  • 初学学习408之数据结构--数据结构基本概念
  • Java项目中必须使用本地缓存的几种情况
  • 【鸿蒙 HarmonyOS 4.0】TypeScript开发语言
  • Android java基础_异常