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案例实战-自定义按钮实现(带图片文字虚线实线边框切换)

相关推荐
fangzhanpeng1687 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心9 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552715 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552716 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝17 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552717 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552718 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552720 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552720 天前
Java I/O核心操作实战
人工智能·1024程序员节
u01030552721 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节