WPF+MVVM案例实战(二)-自定义按钮实现(带图片文字虚线实线边框切换)

### 文章目录

  • [@[TOC](文章目录)](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [1、创建项目](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [2、创建自定义控件类库](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [3、实现自定义控件](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [1. ImageTextButton 依赖属性实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [2.样式模板实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [4、引用自定义控件库](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [5、UI及功能实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [1、 前端UI实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [2、状态转换器 InverseBooleanConverter 实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [3、MainViewModel.cs 实现](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [6、运行效果](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)
  • [7、源代码获取](#文章目录 @TOC 1、创建项目 2、创建自定义控件类库 3、实现自定义控件 1. ImageTextButton 依赖属性实现 2.样式模板实现 4、引用自定义控件库 5、UI及功能实现 1、 前端UI实现 2、状态转换器 InverseBooleanConverter 实现 3、MainViewModel.cs 实现 6、运行效果 7、源代码获取)

1、创建项目

打开 VS2022 ,新建项目 Wpf_Examples,创建各层级文件夹,安装 CommunityToolkit.Mvvm 和 Microsoft.Extensions.DependencyInjectio NuGet包,完成MVVM框架搭建。搭建完成后项目层次如下图所示:

这里如何实现 MVVM 框架可以参考本人 像 MvvmLight 一样使用 CommunityToolkit.Mvvm 工具包 的文章

2、创建自定义控件类库

新建WPF自定义控件类库 CustomControlLib ,注意是自定义控件类库,不是用户控件库,如下图所示:

3、实现自定义控件

1. ImageTextButton 依赖属性实现

代码如下(示例):

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace CustomControlLib
{
    public class ImageTextButton : Button
    {
        public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ImageTextButton));

        public ImageSource ImageSource
        {
            get => (ImageSource)GetValue(ImageSourceProperty);
            set => SetValue(ImageSourceProperty, value);
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(ImageTextButton));

        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }

        public static readonly DependencyProperty IsDashedBorderProperty =
            DependencyProperty.Register("IsDashedBorder", typeof(bool), typeof(ImageTextButton), new PropertyMetadata(false, OnIsDashedBorderChanged));

        public bool IsDashedBorder
        {
            get => (bool)GetValue(IsDashedBorderProperty);
            set => SetValue(IsDashedBorderProperty, value);
        }

        private static void OnIsDashedBorderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ImageTextButton)d).InvalidateVisual();
        }

        public ImageTextButton()
        {
            DefaultStyleKey = typeof(ImageTextButton);
        }
    }
}

2.样式模板实现

代码如下(示例):

csharp 复制代码
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomControlLib">
    <Style TargetType="{x:Type local:ImageTextButton}">
        <Setter Property="Width" Value="60"/>
        <Setter Property="Height" Value="60"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ImageTextButton}">
                    <Border x:Name="Border"
                     BorderBrush="Gray"
                     BorderThickness="1"
                     CornerRadius="8"
                     Background="Transparent"
                     SnapsToDevicePixels="true" Opacity="1" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="65*"/>
                                <RowDefinition Height="35*"/>
                            </Grid.RowDefinitions>
                            <Image x:Name="Image" Grid.Row="0"
                            Source="{TemplateBinding ImageSource}"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center" Margin="0 5 0 0" Opacity="1"/>
                            <TextBlock x:Name="TextBlock" Grid.Row="1"
                                Text="{TemplateBinding Text}"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                FontSize="14"
                                Foreground="Black" Opacity="1"/>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="LightGray" TargetName="Border"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Background" Value="DarkGray" TargetName="Border"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Foreground" Value="Gray"/>
                            <Setter Property="Background" Value="LightGray" TargetName="Border"/>
                        </Trigger>
                        <Trigger Property="IsDashedBorder" Value="True">
                            <Setter Property="IsEnabled" Value="false"/>
                            <Setter Property="Opacity" Value="0.6" TargetName="Border"/>
                            <Setter Property="Opacity" Value="0.6" TargetName="Image"/>
                            <Setter Property="Opacity" Value="0.6" TargetName="TextBlock"/>
                            <Setter Property="Foreground" Value="Gray"/>
                            <Setter Property="BorderBrush" TargetName="Border">
                                <Setter.Value>
                                    <VisualBrush Stretch="Fill" TileMode="Tile">
                                        <VisualBrush.Visual>
                                            <Rectangle StrokeDashArray="2 1" Stroke="Gray" StrokeThickness="1" Width="50" Height="50"/>
                                        </VisualBrush.Visual>
                                    </VisualBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

4、引用自定义控件库

将自定义控件库添加到项目引用,如下所示:

5、UI及功能实现

1、 前端UI实现

MainWindow.xaml 代码如下所示:

csharp 复制代码
<Window x:Class="Wpf_Examples.MainWindow"
        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:converter="clr-namespace:Wpf_Examples.Converters"
        xmlns:local="clr-namespace:Wpf_Examples"
        xmlns:cc="clr-namespace:CustomControlLib;assembly=CustomControlLib"
        DataContext="{Binding Source={StaticResource Locator},Path=Main}"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <converter:StatusToColorConverter x:Key="StatusToColorConverter"/>
        <converter:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Center"  HorizontalAlignment="Right">
            <TextBlock Text="双按钮状态控制,边框同时虚线实线切换" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 0 80 0"/>
            <cc:ImageTextButton Text="开始生产" ToolTip="开始生产"  IsDashedBorder="{Binding SystemStatus}" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=ToolTip}" Width="70" Height="70" Margin="0,0,5,0" ImageSource="pack://application:,,,/Wpf_Examples;component/Assets/Images/Start.png"/>
            <cc:ImageTextButton Text="停止生产" ToolTip="停止生产"   IsDashedBorder="{Binding SystemStatus,Converter={StaticResource InverseBooleanConverter}}" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=ToolTip}" Width="70" Height="70" Margin="0,0,5,0" ImageSource="pack://application:,,,/Wpf_Examples;component/Assets/Images/Stop.png"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" VerticalAlignment="Center"  HorizontalAlignment="Right" Grid.Row="1">
            <TextBlock Text="单按钮状态控制,切换背景图片和文本" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 0 80 0"/>
            <cc:ImageTextButton Text="{Binding ButtonName}" Command="{Binding SingleButtonClickCmd}" ImageSource="{Binding ImageSource}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Text}" Width="70" Height="70" Margin="0,0,5,0" />
        </StackPanel>

        <Grid Grid.Row="3">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right">
                <TextBlock Text="网络" FontSize="16" Foreground="DarkGray" Margin="0 0 20 0"/>
                <Ellipse Width="20" Height="20" Fill="{Binding NetStatusValue, Converter={StaticResource StatusToColorConverter}}"/>
            </StackPanel>
            <StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
                <TextBlock Text="PLC" FontSize="16" Foreground="DarkGray" Margin="0 0 20 0"/>
                <Ellipse Width="20" Height="20" Fill="{Binding PLCStatusValue, Converter={StaticResource StatusToColorConverter}}"/>
            </StackPanel>
            <StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
                <TextBlock Text="相机" FontSize="16" Foreground="DarkGray" Margin="0 0 20 0"/>
                <Ellipse Width="20" Height="20" Fill="{Binding DevStatusValue, Converter={StaticResource StatusToColorConverter}}"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>

2、状态转换器 InverseBooleanConverter 实现

代码如下:

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace Wpf_Examples.Converters
{
    public class InverseBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return !(value is bool boolValue) || !boolValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return !(value is bool boolValue) || !boolValue;
        }
    }
}

3、MainViewModel.cs 实现

代码如下所示:

csharp 复制代码
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.Media.Imaging;
using System.Windows.Threading;

namespace Wpf_Examples.ViewModels
{
    public class MainViewModel : ObservableObject
    {

        /// <summary>
        /// 网络状态按钮名称
        /// </summary>
        private int netStatusValue = 2;
        public int NetStatusValue
        {
            get { return netStatusValue; }
            set { SetProperty(ref netStatusValue, value); }
        }
        /// <summary>
        /// PLC状态按钮名称
        /// </summary>
        private int plcStatusValue = 1;
        public int PLCStatusValue
        {
            get { return plcStatusValue; }
            set { SetProperty(ref plcStatusValue, value); }
        }
        /// <summary>
        /// 设备状态
        /// </summary>
        private int devStatusValue = 0;
        public int DevStatusValue
        {
            get { return devStatusValue; }
            set { SetProperty(ref devStatusValue, value); }
        }

        /// <summary>
        /// 生产状态按钮名称
        /// </summary>
        private string buttonName;
        public string ButtonName
        {
            get { return buttonName; }
            set { SetProperty(ref buttonName, value); }
        }

        /// <summary>
        /// 系统运行状态
        /// </summary>
        private bool systemStatus = false;
        public bool SystemStatus
        {
            get { return systemStatus; }
            set { SetProperty(ref systemStatus, value); }
        }
        /// <summary>
        /// 产线状态
        /// </summary>
        private bool productStatus = false;
        public bool ProductStatus
        {
            get { return productStatus; }
            set { SetProperty(ref productStatus, value); }
        }


        /// <summary>
        /// 生产状态背景图
        /// </summary>
        private BitmapImage imageSource;
        public BitmapImage ImageSource
        {
            get { return imageSource; }
            set { SetProperty(ref imageSource, value); }
        }




        public RelayCommand<string> ButtonClickCmd { get; set; }
        public RelayCommand SingleButtonClickCmd { get; set; }

        public MainViewModel()
        {
            ButtonClickCmd = new RelayCommand<string>(FunMenu);
            SingleButtonClickCmd = new RelayCommand(StatusChange);
            CreateTimer();
            StatusChange();
        }

        private void StatusChange()
        {
            if (!ProductStatus)
            {
                ButtonName = "开始生产";
                ImageSource = new BitmapImage(new Uri("pack://application:,,,/Wpf_Examples;component/Assets/Images/Start.png"));
                ProductStatus = true;
            }
            else
            {
                ButtonName = "停止生产";
                ImageSource = new BitmapImage(new Uri("pack://application:,,,/Wpf_Examples;component/Assets/Images/Stop.png"));
                ProductStatus = false;
            }

        }

        private void FunMenu(string obj)
        {
            string menu = obj as string;
            switch (menu)
            {
                case "开始生产":
                    SystemStatus = true;
                    break;
                case "停止生产":
                    SystemStatus = false;
                    break;
            }
        }

        private void CreateTimer()
        {
            #region 每秒定时器服务
            DispatcherTimer cpuTimer = new DispatcherTimer
            {
                Interval = new TimeSpan(0, 0, 0, 3, 0)
            };
            cpuTimer.Tick += DispatcherTimer_Tick;
            cpuTimer.Start();
            #endregion
        }
        private void DispatcherTimer_Tick(object sender, EventArgs e)
        {

            DevStatusValue = StatusChange(DevStatusValue);
            NetStatusValue = StatusChange(NetStatusValue);
            PLCStatusValue = StatusChange(PLCStatusValue);
        }


        private int StatusChange(int value)
        {
            int outVal = 0;
            //状态变化
            if (value == 0)
            {
                outVal = 1;
            }
            else if (value == 1)
            {
                outVal = 2;
            }
            else
            {
                outVal = 0;
            }
            return outVal;
        }
    }
}

6、运行效果

7、源代码获取

CSDN链接:WPF+MVVM案例实战-自定义按钮实现(带图片文字虚线实线边框切换)

相关推荐
尘佑不尘21 分钟前
shodan5,参数使用,批量查找Mongodb未授权登录,jenkins批量挖掘
数据库·笔记·mongodb·web安全·jenkins·1024程序员节
SeniorMao0071 小时前
结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统
1024程序员节
网安_秋刀鱼1 小时前
CSRF防范及绕过
前端·安全·web安全·网络安全·csrf·1024程序员节
WW、forever1 小时前
【ArcGIS Pro实操第4期】绘制三维地图
1024程序员节
记录学习-python1 小时前
Django-cookie,session
1024程序员节
b21431241 小时前
【运动的&足球】足球运动员球守门员裁判检测系统源码&数据集全套:改进yolo11-DBBNCSPELAN
1024程序员节
聪明的墨菲特i2 小时前
Vue组件学习 | 二、Vuex组件
前端·vue.js·学习·前端框架·1024程序员节
长潇若雪2 小时前
结构体(C 语言)
c语言·开发语言·经验分享·1024程序员节
DARLING Zero two♡3 小时前
关于我、重生到500年前凭借C语言改变世界科技vlog.12——深入理解指针(2)
c语言·开发语言·科技·1024程序员节
独行soc3 小时前
#渗透测试#SRC漏洞挖掘# 信息收集-Shodan进阶之Jenkins组件
安全·jenkins·安全威胁分析·1024程序员节·shodan