如何隐藏 Visual Studio 中自定义工具生成的文件
我希望隐藏自定义工具生成的文件,但我找不到任何有关如何完成此操作的文档。
我正在寻找的一个示例是文件背后的 WPF 代码。这些文件不会显示在 Visual Studio 项目视图中,但会与项目一起编译并在 IntelliSense 中可用。 WPF 代码隐藏文件(例如 Window1.gics)由自定义工具生成。
I would like the files generated by my custom tool to be hidden, but I cannot find any documentation on how this is done.
An example of what I'm looking for is WPF code behind files. These files are not displayed in the Visual Studio project view, yet are compiled with the project and are available in IntelliSense. WPF code behind files (Window1.g.i.cs, for example), are generated by a custom tool.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
解决方案是创建一个目标,将文件添加到 Compile ItemGroup,而不是将它们显式添加到 .csproj 文件中。这样,Intellisense 将看到它们,并将它们编译到可执行文件中,但它们不会显示在 Visual Studio 中。
简单示例
您还需要确保将目标添加到
CoreCompileDependsOn
属性中,以便它将在编译器运行之前执行。这是一个非常简单的示例:
如果您将其添加到 .csproj 文件的底部(就在
之前),您的“HiddenFile.cs”将包含在您的编译中,即使它不会出现在 Visual Studio 中。
使用单独的 .targets 文件
通常,您不会将其直接放置在 .csproj 文件中,而是将其放置在一个单独的 .targets 文件中,周围是:
并使用
< 导入到 .csproj 中。导入项目=“MyTool.targets”>
。即使对于一次性情况,也建议使用 .targets 文件,因为它将自定义代码与 Visual Studio 维护的 .csproj 中的内容分开。构造生成的文件名
如果您正在创建通用工具和/或使用单独的 .targets 文件,您可能不想显式列出每个隐藏文件。相反,您希望从项目中的其他设置生成隐藏文件名。例如,如果您希望所有资源文件在“obj”目录中都有相应的工具生成的文件,则您的目标将是:
“IntermediateOutputPath”属性就是我们都知道的“obj”目录,但如果最终用户您的 .targets 已对此进行了自定义,您的中间文件仍将在同一位置找到。如果您希望生成的文件位于主项目目录中而不是“obj”目录中,则可以将其保留。
如果您只想通过自定义工具处理现有项目类型的部分文件?例如,您可能想要为所有带有“.xyz”扩展名的页面和资源文件生成文件。
请注意,您不能在顶级 ItemGroup 中使用 %(Extension) 等元数据语法,但可以在 Target 中执行此操作。
使用自定义项目类型(也称为生成操作)
以上处理具有现有项目类型(例如页面、资源或编译)的文件(Visual Studio 将此称为“生成操作”)。如果您的项目是一种新类型的文件,您可以使用自己的自定义项目类型。例如,如果您的输入文件名为“Xyz”文件,则您的项目文件可以将“Xyz”定义为有效的项类型:
之后 Visual Studio 将允许您在文件属性的“构建操作”中选择“Xyz”,从而导致将此添加到您的 .csproj:
现在您可以使用“Xyz”项目类型来创建工具输出的文件名,就像我们之前使用“资源”项目类型所做的那样:
当使用自定义项目类型时,您可以使您的项目也可以通过内置机制将它们映射到另一个项目类型(也称为构建操作)来处理。如果您的“Xyz”文件实际上是 .cs 文件或 .xaml,或者需要将它们制作
为嵌入式资源,则这非常有用。例如,您可以使所有具有 Xyz 的“Build Action”的文件也被编译:
或者,如果您的“Xyz”源文件应存储为嵌入式资源,您可以这样表达:
请注意,第二个示例将不起作用如果将其放入目标中,因为直到核心编译之前才会评估目标。要使其在 Target 内工作,您必须在 PrepareForBuildDependsOn 属性而不是 CoreCompileDependsOn 中列出目标名称。
从 MSBuild 调用自定义代码生成器
在创建 .targets 文件之后,您可能会考虑直接从 MSBuild 调用您的工具,而不是使用单独的预构建事件或 Visual Studio 有缺陷的“自定义工具” “ 机制。
为此,请执行以下操作:
UsingTask
元素添加到您的 .targets 文件中,并在您的目标中添加对新任务的调用这里是实现 ITask 所需的全部内容:
这里是 usingTask 元素和调用它的 Target:
请注意,此目标的 Output 元素将输出文件列表直接放入 Compile 中,因此无需使用单独的 ItemGroup 来执行此操作。
旧的“自定义工具”机制有何缺陷以及为什么不使用它
关于 Visual Studio 的“自定义工具”机制的说明:在 NET Framework 1.x 中,我们没有 MSBuild,因此我们有依靠 Visual Studio 来构建我们的项目。为了对生成的代码进行智能感知,Visual Studio 有一种称为“自定义工具”的机制,可以在文件的“属性”窗口中进行设置。该机制在多个方面存在根本性缺陷,这就是它被 MSBuild 目标取代的原因。 “自定义工具”功能的一些问题是:
如果您使用旧的“自定义工具”功能,我强烈建议您改用 MSBuild 任务。它与 Intellisense 配合良好,允许您构建项目,甚至无需安装 Visual Studio(您只需要 NET Framework)。
您的自定义构建任务何时运行?
一般来说,您的自定义构建任务将运行:
更准确地说:
您可能希望强制生成器在其他时间运行,例如当某些环境变量发生更改时,或者强制它同步运行而不是在后台运行。
要使生成器在没有输入文件发生更改的情况下重新运行,最好的方法通常是向目标添加一个额外的输入,该输入是存储在“obj”目录中的虚拟输入文件。然后,每当环境变量或某些外部设置发生更改而导致生成器工具重新运行时,只需触摸此文件(即创建它或更新其修改日期)即可。
要强制生成器同步运行而不是等待 IntelliSense 在后台运行它,只需使用 MSBuild 来构建您的特定目标。这可以像执行“MSBuild /t:GenerateToolOutput”一样简单,或者 VSIP 可以提供一种内置方法来调用自定义构建目标。或者,您可以简单地调用 Build 命令并等待它完成。
请注意,本节中的“输入文件”指的是 Target 元素的“Inputs”属性中列出的任何内容。
最后注意事项
您可能会收到来自 Visual Studio 的警告,表明它不知道是否信任您的自定义工具 .targets 文件。要解决此问题,请将其添加到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MSBuild\SafeImports 注册表项。
以下是实际 .targets 文件的所有部分都已就位后的样子的摘要:
如果您有任何问题或有任何不明白的地方,请告诉我。
The solution is to create a Target that adds your files to the Compile ItemGroup rather than adding them explicitly in your .csproj file. That way Intellisense will see them and they will be compiled into your executable, but they will not show up in Visual Studio.
Simple example
You also need to make sure your target is added to the
CoreCompileDependsOn
property so it will execute before the compiler runs.Here is an extremely simple example:
If you add this to the bottom of your .csproj file (just before
</Project>
), your "HiddenFile.cs" will be included in your compilation even though it doesn't appear in Visual Studio.Using a separate .targets file
Instead of placing this directly in your .csproj file, you would generally placed it in a separate .targets file surrounded by:
and import into your .csproj with
<Import Project="MyTool.targets">
. A .targets file is recommended even for one-off cases because it separates your custom code from the stuff in .csproj that is maintained by Visual Studio.Constructing the generated filename(s)
If you are creating a generalized tool and/or using a separate .targets file, you probably don't want to explicitly list each hidden file. Instead you want to generate the hidden file names from other settings in the project. For example if you want all Resource files to have corresponding tool-generated files in the "obj" directory, your Target would be:
The "IntermediateOutputPath" property is what we all know as the "obj" directory, but if the end-user of your .targets has customized this your intermediate files will stil be found in the same place. If you prefer your generated files to be in the main project directory and not in the "obj" directory, you can leave this off.
If you want only some of the files of an existing item type to be processed by your custom tool? For example, you may want to generate files for all Page and Resource files with a ".xyz" extension.
Note that you can't use the metadata syntax like %(Extension) in a top-level ItemGroup but you can do so within a Target.
Using a custom item type (aka Build Action)
The above processes files that have an existing item type such as Page, Resource, or Compile (Visual Studio calls this the "Build Action"). If your items are a new kind of file you can use your own custom item type. For example if your input files are called "Xyz" files, your project file can define "Xyz" as a valid item type:
after which Visual Studio will allow you to select "Xyz" in the Build Action in the file's properties, resulting in this being added to your .csproj:
Now you can use the "Xyz" item type to create the filenames for tool output, just as we did previously with the "Resource" item type:
When using a custom item type you can cause your items to also be handled by built-in mechanisms by mapping them to another item type (aka Build Action). This is useful if your "Xyz" files are really .cs files or .xaml or if they need to be made
EmbeddedResources. For example you can cause all files with "Build Action" of Xyz to also be compiled:
Or if your "Xyz" source files should be stored as embedded resources, you can express it this way:
Note that the second example won't work if you put it inside the Target, since the target isn't evaluated until just before the core compile. To make this work inside a Target you have to list the target name in PrepareForBuildDependsOn property instead of CoreCompileDependsOn.
Invoking your custom code generator from MSBuild
Having gone as far as creating a .targets file, you might consider invoking your tool directly from MSBuild rather than using a separate pre-build event or Visual Studio's flawed "Custom Tool" mechanism.
To do this:
UsingTask
element to your .targets file, and in your target add a call to your new taskHere is all you need to implement ITask:
And here is the UsingTask element and a Target that calls it:
Note that this target's Output element places the list of output files directly into Compile, so there is no need to use a separate ItemGroup to do this.
How the old "Custom Tool" mechanism is flawed and why not to use it
A note on Visual Studio's "Custom Tool" mechanism: In NET Framework 1.x we didn't have MSBuild, so we had to rely on Visual Studio to build our projects. In order to get Intellisense on generated code, Visual Studio had a mechanism called "Custom Tool" that can be set in the Properties window on a file. The mechanism was fundamentally flawed in several ways, which is why it was replaced with MSBuild targets. Some of the problems with the "Custom Tool" feature were:
If you are using the old "Custom Tool" feature, I strongly recommend you switch to using a MSBuild task. It works well with Intellisense and allows you to build your project without even installing Visual Studio (all you need is NET Framework).
When will your custom build task run?
In general your custom build task will run:
To be more precise:
You may want to force your generator to run at other times, such as when some environment variable changes, or force it to run synchronously rather in the background.
To cause the generator to re-run even when no input files have changed, the best way is usually to add an additional Input to your Target which is a dummy input file stored in the "obj" directory. Then whenever an environment variable or some external setting changes that should force your generator tool to re-run, simply touch this file (ie. create it or update its modified date).
To force the generator to run synchronously rather than waiting for IntelliSense to run it in the background, just use MSBuild to build your particular target. This could be as simple as executing "MSBuild /t:GenerateToolOutput", or VSIP may provide a build-in way to call custom build targets. Alternatively you could simply invoke the Build command and wait for it to complete.
Note that "Input files" in this section refers to whatever is listed in the "Inputs" attribute of the Target element.
Final notes
You may get warnings from Visual Studio that it doesn't know whether to trust your custom tool .targets file. To fix this, add it to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MSBuild\SafeImports registry key.
Here is a summary of what an actual .targets file would look like with all the pieces in place:
Please let me know if you have any questions or there is anything here you didn't understand.
要从 Visual Studio 隐藏项目,请向项目添加
Visible
元数据属性。InProject
元数据显然也能做到这一点。可见:http://msdn.microsoft.com/en -us/library/ms171468(VS.90).aspx
InProject:链接
To hide items from Visual Studio add a
Visible
metadata property to the item. TheInProject
metadata apparently does this too.Visible: http://msdn.microsoft.com/en-us/library/ms171468(VS.90).aspx
InProject: Link
我知道的唯一方法是添加生成的文件以依赖于您希望将其隐藏在 proj 文件中的文件。
例如:
如果您删除了 DependentUpon 元素,则该文件会显示在另一个文件旁边,而不是后面......您的生成器如何添加文件?您能向我们介绍一下这个用例以及您希望它如何工作吗?
The only way I know to do it is to add the generated file to have a dependency on the file you want it hidden behind - in the proj file.
For example:
If you removed the DependentUpon element then the file shows up beside the other file instead of behind it ... how does your generator add the files? can you walk us through the use case and how you would like it to work?
我想你想看看这里: http://msdn.microsoft.com/ en-us/library/ms171453.aspx。
具体来说,是“在执行期间创建项目”部分。
I think you want to look here: http://msdn.microsoft.com/en-us/library/ms171453.aspx.
Specifically, the "Creating Items During Execution" section.