如何通过源代码生成器读取XAML/BAML以生成C#代码?
借助XAML/BAML实现增量源代码生成的可行方案
方案1:将XAML标记为AdditionalText,直接读取原始XAML内容
默认XAML的生成操作是Page,无法被AdditionalTextsProvider捕获,但可以通过修改项目文件(.csproj),让XAML文件同时保留原有编译逻辑并被生成器识别:
<ItemGroup> <!-- 让所有XAML文件同时作为Page和AdditionalText --> <Page Update="**/*.xaml"> <IsAdditionalText>true</IsAdditionalText> </Page> </ItemGroup>
配置完成后,即可在增量生成器中通过AdditionalTextsProvider获取XAML文件,读取原始内容并解析:
public void Initialize(IncrementalGeneratorInitializationContext context) { // 获取所有标记为AdditionalText的XAML文件 var xamlFiles = context.AdditionalTextsProvider .Where(text => text.Path.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)); // 基于XAML内容生成代码 context.RegisterSourceOutput(xamlFiles, (spc, text) => { string xamlContent = text.GetText(spc.CancellationToken)?.ToString(); if (string.IsNullOrEmpty(xamlContent)) return; // 这里添加XAML解析和代码生成逻辑 // 例如解析XAML中的控件结构,生成对应的C#辅助代码 string generatedCode = GenerateCodeFromXaml(xamlContent, text.Path); spc.AddSource($"{Path.GetFileNameWithoutExtension(text.Path)}.g.cs", generatedCode); }); }
方案2:读取嵌入程序集的BAML(适用于依赖项目的XAML)
如果需要处理已编译为BAML并嵌入到程序集的XAML(比如来自依赖库的内容),可以通过CompilationProvider获取编译上下文,遍历元数据引用读取嵌入资源:
public void Initialize(IncrementalGeneratorInitializationContext context) { var compilationWithBaml = context.CompilationProvider .Select((compilation, ct) => { // 遍历所有元数据引用(依赖程序集) foreach (var reference in compilation.References) { if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol assemblySymbol) continue; // 获取程序集的元数据读取器 if (reference is PortableExecutableReference peRef && peRef.GetMetadataReader(out var reader)) { // 遍历嵌入资源,查找BAML文件 foreach (var resource in reader.ManifestResources) { if (resource.Name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { // 读取BAML资源内容 var resourceData = reader.GetBlobBytes(resource.Value); if (resourceData != null) { // 使用XamlReader解析BAML(需引用WPF相关程序集) using var stream = new MemoryStream(resourceData); var bamlReader = new System.Windows.Markup.Baml2006Reader(stream); var xamlObject = System.Windows.Markup.XamlReader.Load(bamlReader); // 基于解析后的对象生成代码 // ... } } } } } return compilation; }); context.RegisterSourceOutput(compilationWithBaml, (spc, _) => { // 输出生成的代码 }); }
注意:当前项目的XAML编译后的BAML不会在增量生成阶段出现在元数据引用中(因为当前项目还未完成编译),此方案仅适用于处理依赖项目的BAML资源。
方案3:通过MSBuild目标传递XAML/BAML数据(复杂场景)
如果上述两种方案无法满足需求,可以自定义MSBuild目标,在编译前将XAML内容或BAML路径写入临时文件,再将临时文件标记为AdditionalText供生成器读取。此方案适合需要对XAML编译流程做自定义处理的场景,但配置复杂度较高。
内容的提问来源于stack exchange,提问作者user3797758
相关产品推荐
相关产品推荐

