WPF 附加属性+控件模板,完成自定义控件。建议观看HandyControl源码

文章目录

相关连接

WPF控件模板(6)
WPF 附加属性
WPF教程:附加属性

前言

今天说服了领导用WPF开发前端,原因就是开发相对来说比较方便,写小项目就不用前后端分离什么的了。反正就是有个机会写WPF了,真开心。我已经写了一年的Uniapp了

需要实现的效果

就是想写一个简单的变色控件。

附加属性

如果想知道附加属性,就得先了解依赖属性。详细的可以看我这篇文章

WPF 用户控件依赖属性赋值

添加附加属性,以Test修改FontSize为例

知道了依赖属性之后,我解释一下附加属性是什么意思。附加属性就是为了方便在原有的控件基础上面进行细微的修改。我们先保证编译通过

附加属性的快捷键是propa

简单给TextBox添加一个附加属性

csharp 复制代码
    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));
        
        
    }

这样我们就能编译通过了。

xml 复制代码
<TextBlock Text="用户"  wpfEx:TextBlockExtension.Test="2"/>

依赖属性使用

依赖属性有两种使用方法

触发器使用

样式定义

xml 复制代码
    <!--一个简单的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>

简单使用

xml 复制代码
 <TextBlock Text="用户"
            wpfEx:TextBlockExtension.Test="10" Style="{StaticResource UserSelection}">
     
 </TextBlock>
 <TextBlock Text="用户"
            wpfEx:TextBlockExtension.Test="20"
            Style="{StaticResource UserSelection}">
 </TextBlock>

直接操控

附加属性修改

csharp 复制代码
//如果想直接操控元素,得在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;
        }
    }));
xml 复制代码
<!--如果想要预览生效需要重新编译一下-->
<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的样式源码。看一下还是挺有收获的。

参考样式代码

xml 复制代码
 <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>

控件模板原型

我们想写一个控件模板,如果不是很熟练,我们就先把控件模板的原型写出来,这样更利于理解。

xml 复制代码
 <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>

控件模板

xml 复制代码
<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

修改好的效果

xml 复制代码
<!--一个简单的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>

简单使用

xml 复制代码
<RadioButton Content="TIM登录"
             GroupName="UserSelect"
             Style="{StaticResource UserSelection}"
             Foreground="DeepSkyBlue"
             Background="White"
             hc:IconElement.Geometry="{wpfEx:MaterialGeometry Kind=AbTesting}" />

控件模板触发器

完整样式

xml 复制代码
<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>

简单使用

xml 复制代码
<RadioButton Style="{StaticResource UserSelectioin_Admin}" />
<RadioButton Style="{StaticResource UserSelectioin_User}" />


结论

HandyControl的源码看了真的是打开眼界,但是WPF的Xaml有一个无法在内部简单计算的问题。比如我想Witdh=Height = CornerRadius*2。我可能就要写个触发器了。我后面回去测试一下有没有方法可以在Xaml里面简单计算的。

相关推荐
ljs64827395138 分钟前
VMware 中部署 HDFS 集群环境(Hadoop 3.4.3)完整指南
大数据·hadoop·hdfs
斯文的八宝粥17 小时前
WPF企业内训全程实录(下)
大数据·hadoop·wpf
Zhu75818 小时前
使用腾讯CNB构建Hadoop定制容器镜像
大数据·hadoop·分布式
TTBIGDATA19 小时前
【Ambari Plus】13.Spark 安装
大数据·hadoop·分布式·spark·ambari·sqoop·ambari plus
sunxunyong21 小时前
Hadoop租户创建
大数据·hadoop·分布式
泰克教育官方账号1 天前
泰涨知识 | Hive集群环境部署
数据仓库·hive·hadoop
心中有国也有家2 天前
E-Brufen 技术选型全景:Flutter + 鸿蒙 + Hive CE
hive·hadoop·学习·flutter·华为·harmonyos
DataPulse*2 天前
Hive常用参数调优十二板斧
hive·hadoop·硬件架构
TTBIGDATA3 天前
【Ambari Plus】12.Flink 安装
hive·hadoop·flink·ambari·hdp·cdh·bigtop
2601_962341303 天前
计算机毕业设计之jsp考研在线复习平台
java·大数据·开发语言·hadoop·python·考研·课程设计