如何使用MSBuild和XSLT实现条件XAML编译?

问题描述 投票:0回答:1

按照这个问题,我想根据预处理器指令在构建时有条件地包含/排除 XAML 元素,类似于 #if/#else

 在 C# 中的工作方式。我尝试过使用 XSLT 来转换 XAML,但它没有按预期工作。

我想要实现的目标

我在我的

.csproj

中定义预处理器常量:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DefineConstants>DEBUG;TRACE;fix_issue_001</DefineConstants> </PropertyGroup>
然后在 XAML 中,我想有条件地包含基于这些常量的元素:

<Window x:Class="XamlPreprocessDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="450" Width="800"> <StackPanel> <!-- fix_issue_001:true --> <Button Content="Original version" Click="Button_Click" /> <!-- end fix_issue_001:true --> <!-- fix_issue_001:false --> <Button Content="New version" Background="LightGreen" Click="Button_Click" /> <!-- end fix_issue_001:false --> </StackPanel> </Window>
我尝试的解决方案

这是我的 XSLT (Transform.xslt):

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:param name="DefineConstants" select="''"/> <!-- Identity transform --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- Skip end markers --> <xsl:template match="comment()[starts-with(normalize-space(.), 'end fix_')]"/> <!-- Handle conditional comments --> <xsl:template match="comment()[starts-with(normalize-space(.), 'fix_')]"> <xsl:variable name="content" select="normalize-space(.)"/> <xsl:variable name="endMarker" select="concat('end ', substring-before($content, ':'))"/> <xsl:choose> <!-- True condition --> <xsl:when test="contains($content, ':true')"> <xsl:variable name="constant" select="substring-before($content, ':true')"/> <xsl:if test="contains($DefineConstants, $constant)"> <xsl:variable name="endComment" select="following-sibling::comment() [normalize-space(.) = concat('end ', $constant, ':true')][1]"/> <xsl:apply-templates select="following-sibling::node() [generate-id(preceding-sibling::comment() [contains(., concat($constant, ':true'))][1]) = generate-id(current()) and following-sibling::comment() [normalize-space(.) = concat('end ', $constant, ':true')][1] and generate-id(.) != generate-id($endComment)]"/> </xsl:if> </xsl:when> <!-- False condition --> <xsl:when test="contains($content, ':false')"> <xsl:variable name="constant" select="substring-before($content, ':false')"/> <xsl:if test="not(contains($DefineConstants, $constant))"> <xsl:variable name="endComment" select="following-sibling::comment() [normalize-space(.) = concat('end ', $constant, ':false')][1]"/> <xsl:apply-templates select="following-sibling::node() [generate-id(preceding-sibling::comment() [contains(., concat($constant, ':false'))][1]) = generate-id(current()) and following-sibling::comment() [normalize-space(.) = concat('end ', $constant, ':false')][1] and generate-id(.) != generate-id($endComment)]"/> </xsl:if> </xsl:when> </xsl:choose> </xsl:template> </xsl:stylesheet>
这是我的 MSBuild 集成 (XamlPreprocess.targets):

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <UsingTask TaskName="XamlPreprocessTask" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"> <ParameterGroup> <InputFile ParameterType="System.String" Required="true" /> <OutputFile ParameterType="System.String" Required="true" /> <DefineConstants ParameterType="System.String" Required="true" /> <TransformFile ParameterType="System.String" Required="true" /> </ParameterGroup> <Task> <Using Namespace="System"/> <Using Namespace="System.IO"/> <Using Namespace="System.Xml"/> <Using Namespace="System.Xml.Xsl"/> <Code Type="Fragment" Language="cs"> <![CDATA[ try { var transform = new XslCompiledTransform(); transform.Load(TransformFile); var args = new XsltArgumentList(); args.AddParam("DefineConstants", "", DefineConstants); var outputDir = Path.GetDirectoryName(OutputFile); if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir); using (var writer = new XmlTextWriter(OutputFile, System.Text.Encoding.UTF8)) { writer.Formatting = Formatting.Indented; transform.Transform(InputFile, args, writer); } return true; } catch (Exception ex) { Log.LogError($"XAML preprocessing failed: {ex.Message}"); return false; } ]]> </Code> </Task> </UsingTask> <PropertyGroup> <XamlPreprocessDir>$(IntermediateOutputPath)PreprocessedXaml\</XamlPreprocessDir> <XamlTransformFile>$(MSBuildThisFileDirectory)Transform.xslt</XamlTransformFile> </PropertyGroup> <Target Name="PreprocessXaml" BeforeTargets="MarkupCompilePass1;XamlMarkupCompilePass1"> <ItemGroup> <XamlFiles Include="**\*.xaml" Exclude="$(XamlPreprocessDir)**\*.xaml;**\.vshistory\**\*.xaml;App.xaml;bin\**\*.xaml;obj\**\*.xaml" /> </ItemGroup> <MakeDir Directories="$(XamlPreprocessDir)" /> <XamlPreprocessTask InputFile="%(XamlFiles.FullPath)" OutputFile="$(XamlPreprocessDir)%(XamlFiles.RecursiveDir)%(XamlFiles.Filename)%(XamlFiles.Extension)" DefineConstants="$(DefineConstants)" TransformFile="$(XamlTransformFile)"> </XamlPreprocessTask> <!-- Copy the preprocessed files to a temporary backup --> <Copy SourceFiles="@(XamlFiles)" DestinationFiles="@(XamlFiles->'$(XamlPreprocessDir)%(RecursiveDir)%(Filename).original%(Extension)')" /> <!-- Copy the preprocessed files over the originals --> <Copy SourceFiles="@(XamlFiles->'$(XamlPreprocessDir)%(RecursiveDir)%(Filename)%(Extension)')" DestinationFiles="@(XamlFiles)" /> </Target> <Target Name="RestoreXaml" AfterTargets="MarkupCompilePass1;XamlMarkupCompilePass1"> <!-- Restore the original files --> <Copy SourceFiles="@(XamlFiles->'$(XamlPreprocessDir)%(RecursiveDir)%(Filename).original%(Extension)')" DestinationFiles="@(XamlFiles)" /> </Target> <Target Name="CleanPreprocessedXaml" BeforeTargets="Clean"> <RemoveDir Directories="$(XamlPreprocessDir)" /> </Target> </Project>
问题

XSLT 转换似乎没有效果 - 无论是否定义

fix_issue_001

,两个按钮都会显示在最终应用程序中。我希望在定义 
fix_issue_001
 时仅看到“原始版本”按钮,未定义时仅看到“新版本”按钮。

enter image description here

要求

    必须与 Visual Studio 2019 配合使用
  • 应保留 XAML 设计器功能
  • 不应永久修改源文件
  • 与 MSBuild 流程完美集成
问题

    为什么我的 XSLT 转换不起作用?
  1. 有没有更好的方法来实现条件XAML编译?
c# xml wpf xaml xslt
1个回答
0
投票
在 XSLT 3 中,您可以使用类似的代码

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all"> <xsl:accumulator name="collect-to-delete" as="xs:boolean" initial-value="false()"> <xsl:accumulator-rule match="comment()[matches(., '^\s*fix_issue_[0-9]*:false\s*$')]" select="true()"/> <xsl:accumulator-rule match="comment()[matches(., '^\s*end fix_issue_[0-9]*:false\s*$')]" select="false()"/> </xsl:accumulator> <xsl:accumulator name="delete" as="element()*" initial-value="()"> <xsl:accumulator-rule match="*[accumulator-before('collect-to-delete')]" select="$value, ."/> </xsl:accumulator> <xsl:mode on-no-match="shallow-copy" use-accumulators="collect-to-delete delete"/> <xsl:template match="comment()[matches(., '^\s*fix_issue_[0-9]*:(true|false)\s*$')]"/> <xsl:template match="comment()[matches(., '^\s*end fix_issue_[0-9]*:(true|false)\s*$')]"/> <xsl:template match="*[accumulator-before('delete') intersect .]"/> </xsl:stylesheet>
转变

<Window x:Class="XamlPreprocessDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="450" Width="800"> <StackPanel> <!-- fix_issue_001:true --> <Button Content="Original version" Click="Button_Click" /> <!-- end fix_issue_001:true --> <!-- fix_issue_001:false --> <Button Content="New version" Background="LightGreen" Click="Button_Click" /> <!-- end fix_issue_001:false --> </StackPanel> </Window>
进入

<?xml version="1.0" encoding="UTF-8"?><Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="XamlPreprocessDemo.MainWindow" Title="MainWindow" Height="450" Width="800"> <StackPanel> <Button Content="Original version" Click="Button_Click"/> </StackPanel> </Window>
例如,可以使用 .NET Framework/C# 和 Saxon HE 10 运行。而且它应该比 XSLT 1.0 更容易适应和调试。

© www.soinside.com 2019 - 2024. All rights reserved.