1、创建项目
打开 VS2022 ,新建项目 Wpf_Examples,创建各层级文件夹,安装 CommunityToolkit.Mvvm 和 Microsoft.Extensions.DependencyInjectio NuGet包,完成MVVM框架搭建。搭建完成后项目层次如下图所示:
这里如何实现 MVVM 框架可以参考本人 像 MvvmLight 一样使用 CommunityToolkit.Mvvm 工具包 的文章
2、整理项目
1、项目目录层级
在前面该案例前,已经发布了两个案例,为了后续不断推出的新案例,我们先将项目整理成一个整体,做成整套案例系统,后续所有的案例都在该项目上迭代更新。目前先做简单的归纳处理,我们把所有案例都单独放在一个窗体,主窗体做按钮菜单,每个案例通过弹窗方式呈现。基于以上整理后得到系统文件层级分布如下所示:
2、案例代码实现
1、主界面菜单实现
1、MainWindow.xaml 代码
<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">
<Grid>
<WrapPanel>
<Button Width="120" Height="30" FontSize="18" Content="图片按钮" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/>
<Button Width="120" Height="30" FontSize="18" Content="LED效果灯" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/>
<Button Width="120" Height="30" FontSize="18" Content="动态数字卡" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/>
</WrapPanel>
</Grid>
</Window>
2、MainViewWindow. 代码
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 System.Windows.Media.Imaging;
using System.Windows.Threading;
using Wpf_Examples.Views;
namespace Wpf_Examples.ViewModels
{
public class MainViewModel : ObservableObject
{
public RelayCommand<string> ButtonClickCmd { get; set; }
public MainViewModel()
{
ButtonClickCmd = new RelayCommand<string>(FunMenu);
}
private void FunMenu(string obj)
{
switch (obj)
{
case "图片按钮":
PopWindow(new ImageButtonWindow());
break;
case "LED效果灯":
PopWindow(new LEDStatusWindow());
break;
case "动态数字卡":
PopWindow(new DataCardWindow());
break;
}
}
private void PopWindow(Window window)
{
var mainWindowInstance = App.Current.MainWindow; // 获取主窗口实例
window.Owner = mainWindowInstance;
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
window.ShowDialog();
}
}
}
3、界面效果
2、案例一 图片按钮整理实现
1、ImageButtonWindow.xaml 代码
<Window x:Class="Wpf_Examples.Views.ImageButtonWindow"
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:cc="clr-namespace:CustomControlLib;assembly=CustomControlLib"
xmlns:converter="clr-namespace:Wpf_Examples.Converters"
xmlns:local="clr-namespace:Wpf_Examples.Views"
DataContext="{Binding Source={StaticResource Locator},Path=ImageButton}"
mc:Ignorable="d"
Title="ImageButtonWindow" Height="450" Width="800">
<Window.Resources>
<converter:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</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>
</Window>
2、ImageButtonViewModel.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.Media.Imaging;
namespace Wpf_Examples.ViewModels
{
public class ImageButtonViewModel:ObservableObject
{
/// <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 SingleButtonClickCmd { get; set; }
public RelayCommand<string> ButtonClickCmd { get; set; }
public ImageButtonViewModel()
{
SingleButtonClickCmd = new RelayCommand(StatusChange);
ButtonClickCmd = new RelayCommand<string>(FunMenu);
StatusChange();
}
private void FunMenu(string obj)
{
switch (obj)
{
case "开始生产":
SystemStatus = true;
break;
case "停止生产":
SystemStatus = false;
break;
}
}
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;
}
}
}
}
3、InverseBooleanConverter.cs 转换器代码
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、案例二 LED灯整理实现
1、LEDStatusWindow.xaml 代码
<Window x:Class="Wpf_Examples.Views.LEDStatusWindow"
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.Views"
DataContext="{Binding Source={StaticResource Locator},Path=LedStatus}"
mc:Ignorable="d"
Title="LEDStatusWindow" Height="450" Width="800">
<Window.Resources>
<converter:StatusToColorConverter x:Key="StatusToColorConverter"/>
</Window.Resources>
<Grid>
<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>
</Window>
2、LEDStatusViewModel.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.Threading;
namespace Wpf_Examples.ViewModels
{
public class LEDStatusViewModel: 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 bool systemStatus = false;
public bool SystemStatus
{
get { return systemStatus; }
set { SetProperty(ref systemStatus, value); }
}
public LEDStatusViewModel()
{
CreateTimer();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
DevStatusValue = StatusChange(DevStatusValue);
NetStatusValue = StatusChange(NetStatusValue);
PLCStatusValue = StatusChange(PLCStatusValue);
}
private void CreateTimer()
{
#region 每秒定时器服务
DispatcherTimer cpuTimer = new DispatcherTimer
{
Interval = new TimeSpan(0, 0, 0, 3, 0)
};
cpuTimer.Tick += DispatcherTimer_Tick;
cpuTimer.Start();
#endregion
}
private int StatusChange(int value)
{
int outVal = 0;
//状态变化
if (value == 0)
{
outVal = 1;
}
else if (value == 1)
{
outVal = 2;
}
else
{
outVal = 0;
}
return outVal;
}
}
}
3、StatusToColorConverter.cs 转换器代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
namespace Wpf_Examples.Converters
{
public class StatusToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is int statusValue)
{
switch (statusValue)
{
case 0:
return Brushes.Red;
case 1:
return "#E5D21C";
case 2:
return Brushes.Green;
default:
return Brushes.Green; // 默认颜色
}
}
return Brushes.Gray;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
4、动态数字卡实现
1、DataCardWindow.xaml 界面代码实现
<Window x:Class="Wpf_Examples.Views.DataCardWindow"
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"
DataContext="{Binding Source={StaticResource Locator},Path=DataCard}"
mc:Ignorable="d"
Title="DataCardWindow" Height="450" Width="800" Background="#2B2B2B">
<Grid>
<!--第二列-->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<StackPanel.Resources>
<DataTemplate x:Key="machineCount">
<Border Width="15" Background="#99D40B0B" Margin="2,0">
<TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="16"></TextBlock>
</Border>
</DataTemplate>
</StackPanel.Resources>
<TextBlock Text="机台 总数" Foreground="#99ffffff" Margin="10,0" VerticalAlignment="Center" FontSize="10"></TextBlock>
<ItemsControl ItemsSource="{Binding MachineCount}" ItemTemplate="{StaticResource machineCount}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Text="生产计数" Foreground="#99ffffff" VerticalAlignment="Center" FontSize="10" Margin="20,0"></TextBlock>
<ItemsControl ItemsSource="{Binding ProductCount}" ItemTemplate="{StaticResource machineCount}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Text="不良计数" Foreground="#99ffffff" Margin="20,0" VerticalAlignment="Center" FontSize="10"></TextBlock>
<ItemsControl ItemsSource="{Binding BadCount}" ItemTemplate="{StaticResource machineCount}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</Grid>
</Window>
2、DataCardViewModel.cs 代码实现
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace Wpf_Examples.ViewModels
{
public class DataCardViewModel:ObservableObject
{
#region 计数
/// <summary>
/// 机台总数
/// </summary>
private string _MachineCount = "02981";
/// <summary>
/// 机台总数
/// </summary>
public string MachineCount
{
get { return _MachineCount; }
set
{
SetProperty(ref _MachineCount, value);
}
}
/// <summary>
/// 生产计数
/// </summary>
private string _ProductCount = "16403";
/// <summary>
/// 生产计数
/// </summary>
public string ProductCount
{
get { return _ProductCount; }
set
{
SetProperty(ref _ProductCount, value);
}
}
/// <summary>
/// 不良计数
/// </summary>
private string _BadCount = "0403";
/// <summary>
/// 不良计数
/// </summary>
public string BadCount
{
get { return _BadCount; }
set
{
SetProperty(ref _BadCount, value);
}
}
#endregion
public DataCardViewModel()
{
CreateTimer();
}
private void CreateTimer()
{
#region 每秒定时器服务
DispatcherTimer cpuTimer = new DispatcherTimer
{
Interval = new TimeSpan(0, 0, 0, 2, 0)
};
cpuTimer.Tick += DispatcherTimer_Tick;
cpuTimer.Start();
#endregion
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
ValueChanged();
}
private void ValueChanged()
{
Random random = new Random();
ProductCount= random.Next(100, 9999).ToString();
BadCount = random.Next(100, 999).ToString();
}
}
}
3、效果展示
4.源代码
CSDN下载链接:WPF+MVVM案例实战(三)- 动态数字卡片效果实现