WPF 基于.NET Framework框架和CommunityToolKit.Mvvm包开发一个MVVM结构的上位机
在 .NET Framework 4.7.2 上使用源生成器,最稳定的做法是让项目文件变成 SDK 风格:
-
新建一个 .NET Core / .NET 的 WPF 项目
-
双击项目文件“csproj”,把
TargetFramework改为net472 -
添加
<LangVersion>9.0</LangVersion>(最低 8.0) -
保存并重启 Visual Studio,再通过 NuGet 安装
CommunityToolkit.Mvvm
这样项目天然使用 PackageReference,源生成器会自动挂载。我建立的 .csproj 示例如下
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net472</TargetFramework> <UseWPF>true</UseWPF> <LangVersion>9.0</LangVersion> <RootNamespace>WpfApp1</RootNamespace> <AssemblyName>WpfApp1</AssemblyName> <ProjectGuid>{D4EC347F-A4AB-4CA9-A9E0-3E49FF6E37F1}</ProjectGuid> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <Deterministic>true</Deterministic> </PropertyGroup> <ItemGroup> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> </ItemGroup> </Project>
关于 AssemblyInfo.cs 的注意点
SDK 风格项目会自动生成程序集特性(如 AssemblyTitle、AssemblyVersion)。你项目里原有的 Properties\AssemblyInfo.cs 如果还包含这些特性,会跟自动生成的内容冲突报错(如 CS0579 重复的 Attribute)。
处理方式有两种:
-
推荐:删掉
Properties\AssemblyInfo.cs中那些程序集特性,只保留[assembly: ThemeInfo(...)](WPF 需要它)。 -
或者:在
<PropertyGroup>中加上<GenerateAssemblyInfo>false</GenerateAssemblyInfo>,让 SDK 不自动生成,保留你原有的AssemblyInfo.cs。
如果你想省事,直接加这一句即可:
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
目录结构:

public partial class App : Application { public static ServiceProvider ServiceProvider { get; private set; } protected override void OnStartup(StartupEventArgs e) { var services = new ServiceCollection(); // 注册 Messenger 为单例,使用默认的 WeakReferenceMessenger services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default); // 注册服务,在MainWindowViewModel的构造函数注入的时候自动实例化一个单例 services.AddSingleton<ILoggerService, LoggerService>(); // 注册 ViewModel services.AddSingleton<MainWindowViewModel>(); ServiceProvider = services.BuildServiceProvider(); base.OnStartup(e); } }
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <StackPanel Margin="20" VerticalAlignment="Center"> <TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" Padding="5" /> <Button Content="发送日志" Command="{Binding SendLogCommand}" Padding="10" /> </StackPanel> </Window>
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 从 IOC 容器解析 ViewModel DataContext = App.ServiceProvider.GetRequiredService<MainWindowViewModel>(); } }
源生成器,通过 [ObservableProperty]、[RelayCommand] 在编译期自动生成属性和命令样板代码,减少手写代码,无反射开销,性能更好。
public partial class MainWindowViewModel : ObservableObject { private readonly ILoggerService _loggerService; [ObservableProperty] private string _inputText = string.Empty; // 通过构造函数注入 ILoggerService public MainWindowViewModel(ILoggerService loggerService) { _loggerService = loggerService; } [RelayCommand] private void SendLog() { _loggerService.Log($"用户输入了: {InputText}"); InputText = string.Empty; // 清空输入框 } }
public interface ILoggerService { void Log(string content); } public class LoggerService : ILoggerService { public LoggerService(IMessenger messenger) { // 订阅 LogMessage,收到后在控制台输出 messenger.Register<LogMessage>(this, (recipient, message) => { Console.WriteLine($"[{message.Timestamp:HH:mm:ss}] {message.Content}"); }); } // 发送日志消息 public void Log(string content) { // 通过 Messenger 发送消息 WeakReferenceMessenger.Default.Send(new LogMessage(content, DateTime.Now)); } }
public class LogMessage { public string Content { get; } public DateTime Timestamp { get; } public LogMessage(string content, DateTime timestamp) { Content = content; Timestamp = timestamp; } }
浙公网安备 33010602011771号