直接上代码,新建一个 c# 的 dll 项目,因为 SourceGenerator 只能是 netstandard2.0,我们先修改项目配置文件,并加上相关的引用,结果如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
</Project>
添加 TestSourceGenerator.cs 文件,内容如下:
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Diagnostics;
namespace TestGenerator;
public class TestSyntaxReceiver : ISyntaxReceiver
{
public readonly List<ClassDeclarationSyntax> interfaceSyntaxList = [];
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax syntax)
{
this.interfaceSyntaxList.Add(syntax);
}
}
}
[Generator]
public class TestSourceGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
// 此行去掉注释可调试代码
// Debugger.Launch();
context.RegisterForSyntaxNotifications(() => new TestSyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
if (context.SyntaxReceiver is not TestSyntaxReceiver receiver)
{
return;
}
// 这里可以根据筛选出来的节点,在下面添加相应的代码段。
// var syntaxNodes = receiver.interfaceSyntaxList;
// 本例不解析,直接添加通知代码。
context.AddSource("MainWindowViewModel.Notify.cs", """
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApp1;
public partial class MainWindowViewModel : INotifyPropertyChanged
{
private string _name = string.Empty;
public partial string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
private int _age;
public partial int Age
{
get { return _age; }
set
{
_age = value; OnPropertyChanged(nameof(Age));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
""");
}
}
接下来,我们新建一个wpf 项目,修改项目配置文件为:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MvvmLightLibs" Version="5.4.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TestGenerator\TestGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
</Project>
主要是为了引用刚刚新建的dll项目。
接下来新建一个viewModel文件,文件中使用 partial 属性。
using GalaSoft.MvvmLight.Command;
using System;
using System.Windows.Input;
namespace WpfApp1;
public partial class MainWindowViewModel
{
private RelayCommand? _changeCommand;
public partial string Name { get; set; }
public partial int Age { get; set; }
public ICommand ChangeCommand => _changeCommand ??= new RelayCommand(ChangeName);
private void ChangeName()
{
Name = Guid.NewGuid().ToString();
}
}
然后修改MainWindow.xaml:
<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"
x:Name="win"
d:DataContext="{d:DesignInstance local:MainWindowViewModel}"
Title="MainWindow"
Height="450"
Width="800">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" />
<Button Content="修改"
Command="{Binding ChangeCommand}" />
</StackPanel>
</Window>
在MainWindow.xaml.cs中绑定ViewModel:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
ok,编译 wpf项目。
可以看到,自动生成了一个 MainWindowViewModel.Notify.cs 文件。
运行项目,点击修改按钮,名称已经可以自动通知了。
这样的话,我们不需要在ViewModel中写相应的通知代码,只需要引用此代码生成器项目,便可实现自动通知了。这与CommunityTookit 项目的思路有相似之处。
如果在生成器中加入我们定制化的逻辑,比如查找所有partial property ,给它们都加上通知,这样的话,我们完全不用考虑通知了,而且,ViewModel也不需要实现INotifyPropertyChanged 接口。
自c# 13开始,已经支持分部属性了。如果你发现代码报错了,可以先更新一下vs版本试试。
更多思路,要看道友自行发挥了。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容