WPF 基于.NET Framework框架和CommunityToolKit.Mvvm包开发一个MVVM结构的上位机

在 .NET Framework 4.7.2 上使用源生成器,最稳定的做法是让项目文件变成 SDK 风格:

  1. 新建一个 .NET Core / .NET 的 WPF 项目

  2. 双击项目文件“csproj”,把 TargetFramework 改为 net472

  3. 添加 <LangVersion>9.0</LangVersion>(最低 8.0)

  4. 保存并重启 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>
View Code

关于 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>

  

目录结构:

image

 

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);
    }
}
View Code

 

<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>
View Code

 

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        // 从 IOC 容器解析 ViewModel
        DataContext = App.ServiceProvider.GetRequiredService<MainWindowViewModel>();
    }
}
View Code

源生成器,通过 [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; // 清空输入框
    }
}
View Code

 

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));
    }
}
View Code

 

public class LogMessage
{
    public string Content { get; }
    public DateTime Timestamp { get; }

    public LogMessage(string content, DateTime timestamp)
    {
        Content = content;
        Timestamp = timestamp;
    }
}
View Code

 

posted @ 2026-09-13 20:52  HelloWorld庄先生  阅读(35)  评论(0)    收藏  举报