C# WPF MVVM 标签打印管理软件标准框架结构
下面提供一个完整、可扩展的标准框架结构,涵盖项目分层、目录组织、核心模块,适合作为标签打印管理软件的起点。
一、解决方案整体结构
LabelPrintManager.sln
│
├── src/
│ ├── LabelPrint.Domain/ # 领域层(实体、枚举、接口契约)
│ ├── LabelPrint.Infrastructure/ # 基础设施层(数据访问、打印、日志)
│ ├── LabelPrint.Application/ # 应用层(业务服务、DTO、校验)
│ ├── LabelPrint.WPF/ # 表示层(MVVM、View、ViewModel)
│ └── LabelPrint.Tests/ # 单元测试
│
├── docs/ # 文档
├── build/ # 构建脚本
└── README.md
依赖方向(单向):
WPF → Application → Domain
WPF → Infrastructure → Domain
Application → Infrastructure(通过接口,DI 注入)
二、各层目录结构详解
1. LabelPrint.Domain(领域层)
LabelPrint.Domain/
├── Entities/
│ ├── LabelTemplate.cs # 标签模板
│ ├── LabelElement.cs # 标签元素(文本/条码/二维码/图片)
│ ├── PrintJob.cs # 打印任务
│ ├── PrintRecord.cs # 打印记录
│ ├── PrinterConfig.cs # 打印机配置
│ └── Product.cs # 产品/物料数据源
├── Enums/
│ ├── ElementType.cs # Text/Barcode/QRCode/Image/Line
│ ├── BarcodeType.cs # Code128/EAN13/QRCode...
│ ├── PrintStatus.cs # Pending/Printing/Success/Failed
│ └── PrinterConnectionType.cs# USB/Network/Driver
├── ValueObjects/
│ ├── Position.cs
│ ├── Size.cs
│ └── LabelSize.cs
├── Events/
│ └── PrintCompletedEvent.cs
└── Interfaces/
├── IRepository<T>.cs
├── ILabelTemplateRepository.cs
├── IPrintRecordRepository.cs
├── IPrinterService.cs
└── IBarcodeGenerator.cs
2. LabelPrint.Infrastructure(基础设施层)
LabelPrint.Infrastructure/
├── Data/
│ ├── AppDbContext.cs # EF Core 上下文(SQLite)
│ ├── Configurations/ # Fluent API 配置
│ ├── Repositories/
│ │ ├── RepositoryBase.cs
│ │ ├── LabelTemplateRepository.cs
│ │ └── PrintRecordRepository.cs
│ └── Migrations/
├── Printing/
│ ├── RawPrinterService.cs # 原始指令打印(ZPL/EPL/TSPL)
│ ├── WindowsPrinterService.cs # 系统驱动打印
│ ├── PrinterDetector.cs # 打印机探测
│ └── Commands/
│ ├── ZplCommandBuilder.cs
│ ├── TsplCommandBuilder.cs
│ └── EplCommandBuilder.cs
├── Barcode/
│ └── ZXingBarcodeGenerator.cs # 基于 ZXing.Net
├── Serialization/
│ └── JsonTemplateSerializer.cs
├── Logging/
│ └── SerilogConfigurator.cs
└── DependencyInjection/
└── InfrastructureServiceRegistration.cs
3. LabelPrint.Application(应用层)
LabelPrint.Application/
├── DTOs/
│ ├── LabelTemplateDto.cs
│ ├── PrintJobDto.cs
│ └── PrinterDto.cs
├── Services/
│ ├── ITemplateService.cs / TemplateService.cs
│ ├── IPrintService.cs / PrintService.cs
│ ├── IPrinterManager.cs / PrinterManager.cs
│ └── IDataImportService.cs
├── Mappings/
│ └── AutoMapperProfile.cs
├── Validators/
│ └── LabelTemplateValidator.cs # FluentValidation
├── Common/
│ ├── Result.cs # 统一返回结果
│ └── PagedResult.cs
└── DependencyInjection/
└── ApplicationServiceRegistration.cs
4. LabelPrint.WPF(表示层,MVVM 核心)
LabelPrint.WPF/
├── App.xaml / App.xaml.cs # DI 容器启动
├── AppSettings.json # 配置文件
│
├── Views/
│ ├── MainWindow.xaml # 主窗体(含导航)
│ ├── Pages/
│ │ ├── DashboardView.xaml # 首页
│ │ ├── TemplateDesignerView.xaml # 模板设计器
│ │ ├── TemplateListView.xaml # 模板管理
│ │ ├── PrintCenterView.xaml # 打印中心
│ │ ├── PrintHistoryView.xaml # 打印记录
│ │ ├── PrinterSettingsView.xaml # 打印机设置
│ │ └── SettingsView.xaml # 系统设置
│ ├── Dialogs/
│ │ ├── AddPrinterDialog.xaml
│ │ └── ConfirmDialog.xaml
│ └── Controls/
│ ├── LabelCanvas.xaml # 标签画布控件
│ ├── ElementToolbox.xaml # 元素工具箱
│ └── PropertyPanel.xaml # 属性面板
│
├── ViewModels/
│ ├── MainWindowViewModel.cs
│ ├── Base/
│ │ ├── ViewModelBase.cs # INotifyPropertyChanged
│ │ ├── PageViewModelBase.cs
│ │ └── DialogViewModelBase.cs
│ ├── Pages/
│ │ ├── DashboardViewModel.cs
│ │ ├── TemplateDesignerViewModel.cs
│ │ ├── TemplateListViewModel.cs
│ │ ├── PrintCenterViewModel.cs
│ │ ├── PrintHistoryViewModel.cs
│ │ ├── PrinterSettingsViewModel.cs
│ │ └── SettingsViewModel.cs
│ └── Dialogs/
│ └── AddPrinterDialogViewModel.cs
│
├── Models/ # 仅 UI 状态模型
│ ├── NavigationItem.cs
│ └── CanvasElement.cs
│
├── Services/ # UI 相关服务
│ ├── INavigationService.cs
│ ├── NavigationService.cs
│ ├── IDialogService.cs
│ ├── DialogService.cs
│ ├── IMessageService.cs
│ └── MessageService.cs
│
├── Behaviors/ # 附加行为
│ ├── DragBehavior.cs
│ ├── ResizeBehavior.cs
│ └── SelectionBehavior.cs
│
├── Converters/
│ ├── BoolToVisibilityConverter.cs
│ ├── EnumToStringConverter.cs
│ └── InverseBoolConverter.cs
│
├── Resources/
│ ├── Styles/
│ │ ├── Colors.xaml
│ │ ├── Buttons.xaml
│ │ ├── TextBoxes.xaml
│ │ └── DataGrids.xaml
│ ├── Themes/
│ │ ├── Light.xaml
│ │ └── Dark.xaml
│ └── Icons/
│
├── Helpers/
│ ├── RelayCommand.cs
│ ├── AsyncRelayCommand.cs
│ └── VisualTreeHelperEx.cs
│
└── DependencyInjection/
└── WpfServiceRegistration.cs
三、核心代码骨架
1. 领域实体示例
// Domain/Entities/LabelTemplate.cs
public class LabelTemplate : EntityBase
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public double WidthMm { get; set; }
public double HeightMm { get; set; }
public int Dpi { get; set; } = 203;
public string? BackgroundImagePath { get; set; }
public List<LabelElement> Elements { get; set; } = new();
public DateTime UpdatedAt { get; set; }
}
// Domain/Entities/LabelElement.cs
public class LabelElement
{
public Guid Id { get; set; } = Guid.NewGuid();
public ElementType Type { get; set; }
public double X { get; set; } // mm
public double Y { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Rotation { get; set; }
public string? BindingField { get; set; } // 数据源字段绑定
public string? StaticText { get; set; }
public string FontFamily { get; set; } = "Arial";
public double FontSize { get; set; } = 10;
public bool Bold { get; set; }
public BarcodeType? BarcodeType { get; set; }
}
2. ViewModelBase(MVVM 基础)
// WPF/ViewModels/Base/ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? name = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(name);
return true;
}
public virtual Task InitializeAsync() => Task.CompletedTask;
}
3. RelayCommand
// WPF/Helpers/RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object?> _execute;
private readonly Predicate<object?>? _canExecute;
public RelayCommand(Action<object?> execute, Predicate<object?>? canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? p) => _canExecute?.Invoke(p) ?? true;
public void Execute(object? p) => _execute(p);
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
4. 导航服务
// WPF/Services/INavigationService.cs
public interface INavigationService
{
void NavigateTo<TViewModel>() where TViewModel : PageViewModelBase;
void NavigateTo(string key);
event Action<PageViewModelBase>? Navigated;
}
// WPF/Services/NavigationService.cs
public class NavigationService : INavigationService
{
private readonly IServiceProvider _sp;
private readonly Dictionary<string, Type> _routes = new();
public NavigationService(IServiceProvider sp) => _sp = sp;
public event Action<PageViewModelBase>? Navigated;
public void Register(string key, Type vmType) => _routes[key] = vmType;
public void NavigateTo<TViewModel>() where TViewModel : PageViewModelBase
{
var vm = _sp.GetRequiredService<TViewModel>();
Navigated?.Invoke(vm);
}
public void NavigateTo(string key)
{
if (_routes.TryGetValue(key, out var type))
{
var vm = (PageViewModelBase)_sp.GetRequiredService(type);
Navigated?.Invoke(vm);
}
}
}
5. 打印服务接口 & 实现骨架
// Domain/Interfaces/IPrinterService.cs
public interface IPrinterService
{
Task<IReadOnlyList<PrinterInfo>> GetAvailablePrintersAsync();
Task<bool> PrintAsync(LabelTemplate template, IDictionary<string, string> data, PrinterConfig printer);
}
// Infrastructure/Printing/RawPrinterService.cs
public class RawPrinterService : IPrinterService
{
private readonly ICommandBuilderFactory _builderFactory;
private readonly ILogger<RawPrinterService> _logger;
public RawPrinterService(ICommandBuilderFactory f, ILogger<RawPrinterService> l)
=> (_builderFactory, _logger) = (f, l);
public Task<IReadOnlyList<PrinterInfo>> GetAvailablePrintersAsync()
=> Task.Run(() => PrinterDetector.GetInstalledPrinters().ToList());
public async Task<bool> PrintAsync(LabelTemplate template, IDictionary<string, string> data, PrinterConfig printer)
{
try
{
var builder = _builderFactory.Create(printer.Language);
var commands = builder.Build(template, data);
await RawPrinterHelper.SendBytesToPrinterAsync(printer.Name, commands);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "打印失败");
return false;
}
}
}
6. App.xaml.cs — 依赖注入
public partial class App : Application
{
public static IServiceProvider Services { get; private set; } = null!;
private IHost _host = null!;
protected override async void OnStartup(StartupEventArgs e)
{
_host = Host.CreateDefaultBuilder()
.UseSerilog()
.ConfigureServices((ctx, services) =>
{
services.AddInfrastructure(ctx.Configuration);
services.AddApplication();
services.AddWpfServices();
// 注册 ViewModels
services.AddTransient<MainWindowViewModel>();
services.AddTransient<DashboardViewModel>();
services.AddTransient<TemplateDesignerViewModel>();
services.AddTransient<PrintCenterViewModel>();
// ...
services.AddSingleton<MainWindow>();
})
.Build();
await _host.StartAsync();
Services = _host.Services;
var main = Services.GetRequiredService<MainWindow>();
main.Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await _host.StopAsync();
_host.Dispose();
base.OnExit(e);
}
}
7. MainWindow 导航壳
<!-- Views/MainWindow.xaml -->
<Window x:Class="LabelPrint.WPF.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="标签打印管理" Height="800" Width="1280">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧导航 -->
<ListBox Grid.Column="0" ItemsSource="{Binding NavigationItems}"
SelectedItem="{Binding SelectedNavigation}"
Style="{StaticResource NavListStyle}"/>
<!-- 内容区(DataTemplate 映射 ViewModel → View) -->
<ContentControl Grid.Column="1" Content="{Binding CurrentPage}"/>
</Grid>
</Window>
<!-- App.xaml 中集中注册 View-ViewModel 映射 -->
<Application.Resources>
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
<views:DashboardView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:TemplateDesignerViewModel}">
<views:TemplateDesignerView/>
</DataTemplate>
<!-- ... -->
</Application.Resources>
四、关键设计原则
| 关注点 | 做法 |
|---|---|
| 分层解耦 | Domain 无外部依赖;Application 仅依赖 Domain 接口;Infrastructure 实现接口 |
| 依赖注入 | 使用 Microsoft.Extensions.Hosting 统一管理 |
| ViewModel 定位 View | 通过 DataTemplate 映射,View 不引用 ViewModel |
| 命令绑定 | RelayCommand / AsyncRelayCommand,禁用 code-behind 业务 |
| 通信 | 使用 IMessenger(CommunityToolkit.Mvvm)或自定义 EventAggregator |
| 日志 | Serilog 全局接入 |
| 配置 | AppSettings.json + IOptions<T> |
| 数据库 | SQLite + EF Core,方便部署 |
| 条码 | ZXing.Net 生成图片;打印优先用指令(ZPL/TSPL)直发 |
| 模板序列化 | JSON 存模板,便于版本管理与导入导出 |
| MVVM 工具库 | 推荐 CommunityToolkit.Mvvm(源生成器 [ObservableProperty]/[RelayCommand]) |
五、核心业务流程
1. 模板管理
TemplateListView → TemplateService → Repository → SQLite
2. 模板设计
TemplateDesignerViewModel 维护 Elements 集合
→ Canvas 显示(ItemsControl + Canvas)
→ 拖拽/缩放/属性面板双向绑定
→ 保存为 JSON 入库
3. 打印中心
选择模板 + 选择打印机 + 绑定数据(手工/CSV/数据库)
→ PrintService 渲染数据 → CommandBuilder 生成指令
→ RawPrinterService 通过 Win32 API 直发打印机
→ 写入 PrintRecord
4. 打印历史
PrintHistoryViewModel → PrintRecordRepository → DataGrid 分页
六、NuGet 依赖建议
| 包 | 用途 |
|---|---|
CommunityToolkit.Mvvm |
MVVM 源生成器 |
Microsoft.Extensions.Hosting |
DI / 配置 / 日志宿主 |
Microsoft.EntityFrameworkCore.Sqlite |
数据持久化 |
Serilog.Extensions.Hosting + Serilog.Sinks.File |
日志 |
ZXing.Net + ZXing.Net.Bindings.SkiaSharp |
条码/二维码 |
FluentValidation |
DTO 校验 |
AutoMapper |
DTO 映射 |
MaterialDesignThemes / HandyControl |
可选 UI 主题 |
Newtonsoft.Json / System.Text.Json |
模板序列化 |
七、扩展建议
- 插件化打印指令:
ICommandBuilder+ 工厂,支持 ZPL / TSPL / EPL / CPCL 动态切换。 - 模板版本控制:模板表加
Version字段,保留历史。 - 数据源适配器:
IDataSourceProvider(CSV / Excel / SQL / HTTP)。 - 批量打印:
PrintJob队列 + 后台服务(IHostedService)。 - 权限模块:接入
AspNetCore.Identity或轻量IUserService。 - 国际化:
ResX+ILocalizer。 - 自动更新:Squirrel.Windows 或 ClickOnce。
这个结构可直接作为项目脚手架使用,各层职责清晰、易测试、易替换实现(如把 SQLite 换成 SQL Server、把 ZPL 换成 TSPL)。如需我把上面某一模块(如 模板设计器的画布拖拽 或 ZPL 指令生成器)展开成完整可编译代码,可以继续告诉我。