### 文章目录
- [@[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;
}
}
}