WPF+MVVM案例实战(十七)- 自定义字体图标按钮的封装与实现(ABC类)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 1、案例效果
- 1、按钮分类
- 2、ABC类按钮实现
- 1、文件创建
- 2、字体图标资源
- 3、自定义依赖属性
- 4、按钮特效样式实现
- 3、按钮案例演示
- 1、页面实现与文件创建
- 2、依赖注入
- 3 运行效果
- 4、源代码下载
1、案例效果
1、按钮分类
在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,然后分别实现对应的按钮。
- A【纯文字按钮】 只有文字,但是会根据根据操作改变颜色
- B【纯图片按钮 】只有图片,但是会有图片旋转或者变色特效
- C【文字图片按钮】图片在左,文字在右边,有部分特效
- D【文字图片按钮】图片在右,文字在左边,有部分特效
- E【文字图片按钮】图片在上,文字在下,有部分特效
- F【文字图片按钮】图片在下,文字在上,有部分特效
基本上所有按钮都是上面归纳的情况了,接下来,我们一步一步去实现上面的这些按钮并封装成对应的按钮控件,方便后续使用。
2、ABC类按钮实现
1、文件创建
打开 Wpf_Examples 项目,在自定义控类库中创建文件夹 Buttons ,在Buttons 文件夹下创建 IconFontButton.cs 文件,这里我们将 ABC 三类统称为 字体图标按钮。目录结构如下所示:
2、字体图标资源
想要做出好看的按钮,离不开一个能随时满足自己需求样式的好图标,这里推荐使用阿里巴巴矢量图标库,一款强大免费的图标库,可以搜索到你想要的图标,随时满足你想要的按钮图标,可以创建项目,把每个项目图标单独分类,简直不要太好。每个项目都可以下载 字体资源,也就是 .ttf 格式的子图文件,有了这个文件,我们使用图标就只需要 图标下面的字体代码即可。
3、自定义依赖属性
- PressedBackground - 鼠标按下背景样式类型: Brush默认值: Brushes.DarkBlue
- PressedForeground - 鼠标按下前景样式(图标、文字)类型: Brush默认值:Brushes.White
- MouseOverBackground - 鼠标进入背景样式类型: Brush默认值: Brushes.RoyalBlue
- MouseOverForeground - 鼠标进入前景样式类型: Brush默认值: Brushes.White
- FIcon - 按钮字体图标编码类型: string默认值: “\ue604”
- FIconSize - 按钮字体图标大小类型: int默认值: 20
- FIconMargin - 字体图标间距类型: Thickness默认值: new Thickness(0, 1, 3, 1)
- AllowsAnimation - 是否启用Ficon动画类型: bool 默认值: true
- CornerRadius - 按钮圆角大小, 左上,右上,右下,左下 默认值: 2
- ContentDecorations - 内容装饰集合 类型: TextDecorationCollection 默认值: null
每个依赖属性都有一个对应的属性用于获取和设置其值,并且通过DependencyProperty.Register 方法注册为依赖属性。这些属性允许 IconFontButton 控件根据用户交互或数据变化动态地改变其外观。
代码实现如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace CustomControlLib.Buttons
{
public class IconFontButton : Button
{
public static readonly DependencyProperty PressedBackgroundProperty =
DependencyProperty.Register("PressedBackground", typeof(Brush), typeof(IconFontButton), new PropertyMetadata(Brushes.DarkBlue));
/// <summary>
/// 鼠标按下背景样式
/// </summary>
public Brush PressedBackground
{
get { return (Brush)GetValue(PressedBackgroundProperty); }
set { SetValue(PressedBackgroundProperty, value); }
}
public static readonly DependencyProperty PressedForegroundProperty =
DependencyProperty.Register("PressedForeground", typeof(Brush), typeof(IconFontButton), new PropertyMetadata(Brushes.White));
/// <summary>
/// 鼠标按下前景样式(图标、文字)
/// </summary>
public Brush PressedForeground
{
get { return (Brush)GetValue(PressedForegroundProperty); }
set { SetValue(PressedForegroundProperty, value); }
}
public static readonly DependencyProperty MouseOverBackgroundProperty =
DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(IconFontButton), new PropertyMetadata(Brushes.RoyalBlue));
/// <summary>
/// 鼠标进入背景样式
/// </summary>
public Brush MouseOverBackground
{
get { return (Brush)GetValue(MouseOverBackgroundProperty); }
set { SetValue(MouseOverBackgroundProperty, value); }
}
public static readonly DependencyProperty MouseOverForegroundProperty =
DependencyProperty.Register("MouseOverForeground", typeof(Brush), typeof(IconFontButton), new PropertyMetadata(Brushes.White));
/// <summary>
/// 鼠标进入前景样式
/// </summary>
public Brush MouseOverForeground
{
get { return (Brush)GetValue(MouseOverForegroundProperty); }
set { SetValue(MouseOverForegroundProperty, value); }
}
public static readonly DependencyProperty FIconProperty =
DependencyProperty.Register("FIcon", typeof(string), typeof(IconFontButton), new PropertyMetadata("\ue604"));
/// <summary>
/// 按钮字体图标编码
/// </summary>
public string FIcon
{
get { return (string)GetValue(FIconProperty); }
set { SetValue(FIconProperty, value); }
}
public static readonly DependencyProperty FIconSizeProperty =
DependencyProperty.Register("FIconSize", typeof(int), typeof(IconFontButton), new PropertyMetadata(20));
/// <summary>
/// 按钮字体图标大小
/// </summary>
public int FIconSize
{
get { return (int)GetValue(FIconSizeProperty); }
set { SetValue(FIconSizeProperty, value); }
}
public static readonly DependencyProperty FIconMarginProperty = DependencyProperty.Register(
"FIconMargin", typeof(Thickness), typeof(IconFontButton), new PropertyMetadata(new Thickness(0, 1, 3, 1)));
/// <summary>
/// 字体图标间距
/// </summary>
public Thickness FIconMargin
{
get { return (Thickness)GetValue(FIconMarginProperty); }
set { SetValue(FIconMarginProperty, value); }
}
public static readonly DependencyProperty AllowsAnimationProperty = DependencyProperty.Register(
"AllowsAnimation", typeof(bool), typeof(IconFontButton), new PropertyMetadata(true));
/// <summary>
/// 是否启用Ficon动画
/// </summary>
public bool AllowsAnimation
{
get { return (bool)GetValue(AllowsAnimationProperty); }
set { SetValue(AllowsAnimationProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(IconFontButton), new PropertyMetadata(new CornerRadius(2)));
/// <summary>
/// 按钮圆角大小,左上,右上,右下,左下
/// </summary>
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public static readonly DependencyProperty ContentDecorationsProperty = DependencyProperty.Register(
"ContentDecorations", typeof(TextDecorationCollection), typeof(IconFontButton), new PropertyMetadata(null));
public TextDecorationCollection ContentDecorations
{
get { return (TextDecorationCollection)GetValue(ContentDecorationsProperty); }
set { SetValue(ContentDecorationsProperty, value); }
}
static IconFontButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconFontButton), new FrameworkPropertyMetadata(typeof(IconFontButton)));
}
}
}
4、按钮特效样式实现
在 自定义控件的 Themes 文件夹下创建 Buttons 文件夹,主要存放各种按钮的样式,新建资源样视文件 IconFontButton.xaml ,引用按钮控件如下所示:
然后实现按钮特效样式,这里我把样式分成2个部分实现。
- 1、按钮模板样式 FButton_Template
- 2、按钮属性样式 FButtonStyle
样式代码实现如下所示:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlLib.Buttons"
>
<SolidColorBrush x:Key="ButtonBackground" Color="DimGray"></SolidColorBrush>
<SolidColorBrush x:Key="ButtonForeground" Color="White"></SolidColorBrush>
<!--鼠标在按钮上时按钮背景颜色-->
<SolidColorBrush x:Key="ButtonMouseOverBackground" Color="#93545454"></SolidColorBrush>
<!--鼠标在按钮上时按钮字体颜色-->
<SolidColorBrush x:Key="ButtonMouseOverForeground" Color="#E6E6E6"></SolidColorBrush>
<SolidColorBrush x:Key="ButtonPressedBackground" Color="#2F2F2F"></SolidColorBrush>
<SolidColorBrush x:Key="ButtonPressedForeground" Color="White"></SolidColorBrush>
<Style x:Key="IconFontText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="pack://application:,,,/CustomControlLib;component/Fonts/#iconfont"></Setter>
<Setter Property="Foreground" Value="White"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="20"/>
</Style>
<ControlTemplate x:Key="FButton_Template" TargetType="{x:Type local:IconFontButton}">
<Border x:Name="border" Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= Background}"
Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Height}"
CornerRadius="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CornerRadius}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Width}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
<TextBlock x:Name="icon" Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FIconMargin}"
RenderTransformOrigin="0.5,0.5" Style="{StaticResource IconFontText}"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= FIcon}"
FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= FIconSize}"
Foreground="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= Foreground}">
<TextBlock.RenderTransform>
<RotateTransform x:Name="transIcon" Angle="0"/>
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock VerticalAlignment="Center" x:Name="txt"
TextDecorations="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=ContentDecorations}"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}"
FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize}"
Foreground="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Foreground}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=MouseOverBackground}" TargetName="border" />
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=MouseOverForeground}" TargetName="icon"/>
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=MouseOverForeground}" TargetName="txt"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true"></Condition>
<Condition Property="AllowsAnimation" Value="true"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="transIcon" Storyboard.TargetProperty="Angle" To="180" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="transIcon" Storyboard.TargetProperty="Angle" To="0" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=PressedBackground}" TargetName="border" />
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=PressedForeground}" TargetName="icon"/>
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=PressedForeground}" TargetName="txt"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" TargetName="border"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="FButtonStyle" TargetType="{x:Type local:IconFontButton}">
<Setter Property="Background" Value="{StaticResource ButtonBackground}" />
<Setter Property="Foreground" Value="{StaticResource ButtonForeground}" />
<Setter Property="MouseOverBackground" Value="{StaticResource ButtonMouseOverBackground}" />
<Setter Property="MouseOverForeground" Value="{StaticResource ButtonMouseOverForeground}" />
<Setter Property="PressedBackground" Value="{StaticResource ButtonPressedBackground}" />
<Setter Property="PressedForeground" Value="{StaticResource ButtonPressedForeground}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="30" />
<Setter Property="FontSize" Value="13" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="FIconSize" Value="20" />
<Setter Property="Template" Value="{StaticResource FButton_Template}"/>
<Setter Property="Padding" Value="3,1,3,1" />
<Setter Property="Content" Value="{x:Null}" />
<Setter Property="FIconMargin" Value="0,0,5,0" />
<Setter Property="AllowsAnimation" Value="False" />
</Style>
<Style TargetType="{x:Type local:IconFontButton}" BasedOn="{StaticResource FButtonStyle}"/>
</ResourceDictionary>
以上我们就实现了BC 类的按钮功能,接下来我们逐个使用写出案例。
3、按钮案例演示
1、页面实现与文件创建
打开 Wpf_Examples 项目,在 ViewModels 下创建 ButtonViewModel.cs 文件,Views 文件下创建 ButtonWindow.xaml 窗体。创建完成后如下所示:
ButtonWindow.xaml 代码实现如下:
<Window x:Class="Wpf_Examples.Views.ButtonWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Examples.Views"
xmlns:cc="clr-namespace:CustomControlLib.Buttons;assembly=CustomControlLib"
DataContext="{Binding Source={StaticResource Locator},Path=FButton}"
mc:Ignorable="d"
Title="ButtonWindow" Height="450" Width="800" Background="#2B2B2B">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<GroupBox Header="【纯图片按钮】根据操作改变颜色" Foreground="White">
<StackPanel Orientation="Vertical">
<cc:IconFontButton FIcon="" Margin="5" AllowsAnimation="False" Foreground="Red"/>
<cc:IconFontButton Content="文字按钮" FIcon="" Background="Transparent" AllowsAnimation="True" Foreground="#7ACDE9"/>
<StackPanel Orientation="Horizontal">
<cc:IconFontButton ToolTip="结束" FIcon="" Foreground="Red" Margin="5,0,0,0" CornerRadius="16,0,0,16" AllowsAnimation="True"/>
<cc:IconFontButton ToolTip="播放" FIcon="" Margin="1,0,0,0" CornerRadius="0" AllowsAnimation="True" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=ToolTip}"/>
<cc:IconFontButton ToolTip="暂停" FIcon="" Foreground="Black" Margin="1,0,0,0" CornerRadius="0,16,16,0" AllowsAnimation="True"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="【文字图片按钮】图左字右 图片旋转或者字体变色" Foreground="White">
<StackPanel Orientation="Vertical">
<cc:IconFontButton FIcon="" Foreground="#16D166" AllowsAnimation="True" Content="有动画" />
<cc:IconFontButton FIcon="" Foreground="#FE0000" Margin="0 8 0 0" Content="无动画" />
<StackPanel Orientation="Horizontal" Margin="0 8 0 0">
<cc:IconFontButton ToolTip="咨询" FIcon="" Content="Question" Margin="0 0 1 0"/>
<cc:IconFontButton ToolTip="警告" FIcon="" Foreground="Yellow" Content="Wariing" Margin="0 0 1 0"/>
<cc:IconFontButton ToolTip="错误" FIcon="" Foreground="red" AllowsAnimation="True" Content="Error" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=ToolTip}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
</Grid>
</Window>
ButtonViewModel.cs 代码实现如下:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Wpf_Examples.Views;
namespace Wpf_Examples.ViewModels
{
public class ButtonViewModel:ObservableObject
{
public RelayCommand<string> ButtonClickCmd { get; set; }
public ButtonViewModel() {
ButtonClickCmd = new RelayCommand<string>(BtnFun);
}
private void BtnFun(string obj)
{
switch (obj)
{
case "播放":
MessageBox.Show("播放按钮是假的,不能播放,哈哈哈哈.....","提示",MessageBoxButton.OK);
break;
case "错误":
MessageBox.Show("系统报错了,别怕,假的啦,哈哈哈哈.....", "提示", MessageBoxButton.OK);
break;
}
}
}
}
2、依赖注入
在 ViewModelLocator 文件中实现 按钮界面 与 ViewModel 前后端的绑定,代码如下
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Wpf_Examples.ViewModels
{
public class ViewModelLocator
{
public IServiceProvider Services { get; }
public ViewModelLocator()
{
Services = ConfigureServices();
}
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
//这里实现所有viewModel的容器注入
services.AddSingleton<MainViewModel>();
services.AddTransient<ButtonViewModel>();
//添加其他 viewModel
return services.BuildServiceProvider();
}
public MainViewModel Main => Services.GetService<MainViewModel>();
public ButtonViewModel FButton => Services.GetService<ButtonViewModel>();
}
}
3 运行效果
4、源代码下载
CSDN源代码下载链接 自定义字体图标按钮