我想隐藏我的自定义工具生成的文件,但我找不到任何有关如何完成的文档。如何隐藏由Visual Studio中的自定义工具生成的文件
我正在寻找的一个例子是WPF代码背后的文件。这些文件不会显示在Visual Studio项目视图中,而是与项目一起编译并在IntelliSense中可用。文件后面的WPF代码(例如Window1.g.i.cs)由自定义工具生成。
我想隐藏我的自定义工具生成的文件,但我找不到任何有关如何完成的文档。如何隐藏由Visual Studio中的自定义工具生成的文件
我正在寻找的一个例子是WPF代码背后的文件。这些文件不会显示在Visual Studio项目视图中,而是与项目一起编译并在IntelliSense中可用。文件后面的WPF代码(例如Window1.g.i.cs)由自定义工具生成。
解决方案是创建一个Target,将您的文件添加到Compile ItemGroup,而不是在.csproj文件中显式添加它们。这样Intellisense会看到它们,它们将被编译到您的可执行文件中,但它们不会在Visual Studio中显示。
简单的例子
您还需要确保你的目标是加入CoreCompileDependsOn
属性,因此它会在编译器运行之前运行。
这是一个非常简单的例子:
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="HiddenFile.cs" />
</ItemGroup>
</Target>
如果您添加到您的.csproj文件的底部(之前</Project>
),你的“HiddenFile.cs”将被包括在您的编辑,即使它不会出现在Visual Studio中。
使用一个单独的文件.targets
而是直接在您的.csproj文件放上的,你一般会放置在一个单独的文件.targets包围:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
</Project>
和进口进入你的.csproj与<Import Project="MyTool.targets">
。建议使用.targets文件,即使是一次性案例,因为它将您的自定义代码与由Visual Studio维护的.csproj中的内容分开。
构建生成的文件名(S)
如果要创建一个通用工具和/或使用一个单独的文件.targets,你可能不希望明确列出每个隐藏的文件。相反,您希望从项目中的其他设置生成隐藏的文件名。例如,如果你希望所有的资源文件,必须在“目标文件”目录对应的工具生成的文件,你的目标应该是:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
的“IntermediateOutputPath”属性是我们都知道的“目标文件”目录,但是如果.targets的最终用户已经定制了这个,你的中间文件将会在同一个地方找到。如果你喜欢你生成的文件是在主项目目录,而不是在“目标文件”的目录,你可以离开这个功能。
如果您只想要一些现有项目类型的文件要由您的自定义工具处理?例如,您可能希望为所有页面和资源文件生成带“.xyz”扩展名的文件。
<Target Name="AddToolOutput">
<ItemGroup>
<MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' />
<Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/>
</ItemGroup>
</Target>
请注意,您不能在顶级的ItemGroup使用像%(扩展)元数据的语法,但您可以在目标内这样做。
使用自定义的项目类型(又名生成操作)
有一个现有的项目类型,如页面,资源,或编译(Visual Studio中称此为“建设行动”),上述处理的文件。如果您的项目是一种新的文件,您可以使用自己的自定义项目类型。例如,如果你输入的文件被称为“XYZ”的文件,你的项目文件可以定义“XYZ”作为一个有效的项目类型:
<ItemGroup>
<AvailableItemName Include="Xyz" />
</ItemGroup>
之后,Visual Studio将让你在生成操作选择“XYZ”在文件的属性,导致这个被添加到您的.csproj:
<ItemGroup>
<Xyz Include="Something.xyz" />
</ItemGroup>
现在你可以使用“XYZ”项目类型创建工具输出文件名,就像我们的“资源”以前做项目类型:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
在使用自定义项目类型,你可能会导致你的物品还可以通过内置的机制,将其映射到另一个项目类型(又名生成操作)来处理。如果你的“XYZ”文件是真的.CS文件或的.xaml或者他们需要进行
EmbeddedResources这是非常有用的。例如,你可能会导致的XYZ“建设行动”中的所有文件也被编译:
<ItemGroup>
<Compile Include="@(Xyz)" />
</ItemGroup>
或者,如果你的“XYZ”源文件应该被存储为嵌入的资源,你可以表达这样说:
<ItemGroup>
<EmbeddedResource Include="@(Xyz)" />
</ItemGroup>
注意,第二个例子不会,如果你把它的目标里面,工作,因为目标没有评估,直到只是核心编译之前。为了让你以列出PrepareForBuildDependsOn属性,而不是CoreCompileDependsOn目标名称的目标内这项工作。
MSBuild中
调用自定义代码生成器已经不见了尽可能创造一个.targets文件,你可能会考虑直接从的MSBuild调用你的工具,而不是使用单独的预生成事件或Visual Studio的缺陷“自定义工具”机制。
要做到这一点:
UsingTask
元素添加到您的.targets文件,并在你的目标的调用添加到您的新任务这里是所有你需要实现ITask:
public class GenerateCodeFromXyzFiles : ITask
{
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
public ITaskItem[] InputFiles { get; set; }
public ITaskItem[] OutputFiles { get; set; }
public bool Execute()
{
for(int i=0; i<InputFiles.Length; i++)
File.WriteAllText(OutputFiles[i].ItemSpec,
ProcessXyzFile(
File.ReadAllText(InputFiles[i].ItemSpec)));
}
private string ProcessXyzFile(string xyzFileContents)
{
// Process file and return generated code
}
}
这里是UsingTask元素,并调用它一个目标:
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
注意,这个目标的输出元素放在输出列表文件直接编译成Compile,因此不需要使用单独的ItemGroup来执行此操作。
旧的“自定义工具”机制如何是有缺陷的,为什么不使用它
在Visual Studio中的“自定义工具”机制的说明:在.NET框架1.x中,我们没有MSBuild的,所以我们不得不依靠Visual Studio来构建我们的项目。为了在生成的代码上获得Intellisense,Visual Studio有一个称为“自定义工具”的机制,可以在文件的属性窗口中进行设置。该机制在几个方面存在根本性缺陷,这就是为什么它被MSBuild目标取代的原因。 “自定义工具”功能的一些问题是:
如果您使用旧的“自定义工具”功能,我强烈建议您切换到使用MSBuild任务。它适用于Intellisense,并且允许您在安装Visual Studio的情况下构建您的项目(所有您需要的都是.NET Framework)。
您的自定义构建任务何时运行?
一般来说你的自定义生成任务运行:
为了更准确:在Visual Studio中开始,每次的任何文件被保存在Visual Studio中
您可能要强制发生器,以在其他时间,例如,当一些环境变量的变化运行,或强迫它同步运行,而在后台。
要引起发电机重新运行,即使没有输入文件已经改变,最好的解决方法通常是一个额外的输入添加到您的目标是存储在“目标文件”目录中的虚拟输入文件。然后,只要触及此文件(即创建它或更新其修改的日期),每当环境变量或某些外部设置发生变化(应该强制重新生成生成器工具)时,都可以执行此操作。
要强制生成器同步运行,而不是等待智能感知在后台运行它,只需使用MSBuild构建您的特定目标即可。这可能与执行“MSBuild/t:GenerateToolOutput”一样简单,或者VSIP可以提供内置的方式来调用自定义构建目标。或者,您可以简单地调用Build命令并等待它完成。
请注意,本节中的“输入文件”是指目标元素的“输入”属性中列出的任何内容。
最后说明
你可能会从Visual Studio发出警告,称其不知道是否信任你的自定义工具.targets文件。要解决此问题,请将其添加到HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ VisualStudio \ 9.0 \ MSBuild \ SafeImports注册表项中。
下面是实际的内容摘要。目标文件看起来像全部到位件:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput" Inputs="@(Xyz)" Outputs="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
</Project>
请让我知道如果您有任何问题或有什么在这里,你不明白。
谢谢您的详细回复。只有一个问题:自定义工具方法允许我以编程方式触发从我的VSIP中生成的文件的构建。我能否用你的方法做类似的事情? – jaws 2010-06-23 14:56:55
是的,有几种方法可以控制何时运行自定义构建任务。我添加了一个新的“您的自定义构建任务何时运行?”部分我的答案解释了这是如何工作,并提供了一些选择。 – 2010-06-24 05:51:14
顺便说一句,VS2010使用'HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ VisualStudio \ 10.0 \ MSBuild \ SafeImports'来代替。 – devstuff 2010-07-12 15:39:33
我知道这样做的唯一方法是添加生成的文件,使其对后面隐藏的文件具有依赖关系 - 在proj文件中。
例如:
<ItemGroup>
<Compile Include="test.cs" />
<Compile Include="test.g.i.cs">
<DependentUpon>test.cs</DependentUpon>
</Compile>
</ItemGroup>
如果删除了DependentUpon元素,则文件显示了其他文件,而不是它背后的旁边......请问你的发电机添加文件?你可以引导我们了解用例,并且希望它如何工作?
我想完全隐藏生成的文件。根据定义,自定义工具生成的文件依赖于自定义工具,Visual Studio管理其自己的一代。 – jaws 2010-06-14 12:47:06
我想你想看看这里:http://msdn.microsoft.com/en-us/library/ms171453.aspx。
具体而言,“执行期间创建项目”部分。
该文件需要在设计阶段进行。我需要通过智能感知为用户提供可用的类,就像后面的WPF代码一样。 – jaws 2010-06-18 18:47:13
要从Visual Studio隐藏项目,请将Visible
元数据属性添加到该项目。 InProject
元数据显然也是这样。
可见:http://msdn.microsoft.com/en-us/library/ms171468(VS.90).aspx
InProject:http://blogs.msdn.com/b/jomo_fisher/archive/2005/01/25/360302.aspx
<ItemGroup>
<Compile Include="$(AssemblyInfoPath)">
<!-- either: -->
<InProject>false</InProject>
<!-- or: -->
<Visible>false</Visible>
</Compile>
</ItemGroup>
其中(相对于源文件)你保存生成的文件? – luke 2010-06-02 23:26:20
输出目录与输入目录相同。 – jaws 2010-06-03 14:25:51
当你说WPF代码隐藏文件被隐藏时,你是什么意思?如果我创建一个WPF应用程序,我会得到一个名为MainWindow.xaml的文件,该文件可以展开以显示我认为是代码隐藏文件MainWindow.xaml.cs的内容。 – ErikHeemskerk 2010-06-19 04:31:43