在Visual Studio中跨项目共享WinForms资源的问题

16

我已经将一个大型多项目解决方案中的所有图像资源移动到一个专门用于共享资源的新项目中。

在其他使用共享图像的项目中添加新引用按预期工作。但是,下面所选按钮中的图像在设计器中继续出现问题。

在VS设计器中,我无法在图像编辑器中选择共享资源,而必须手动编辑设计器代码。

更具体地说,在图像编辑器对话框(从现有控件的“图像”属性访问)中,我选择“从文件/资源选择...”以弹出选择资源对话框(如所示)。 在那里,我曾经访问所有本地项目资源(Resources.resx),现在我希望添加对新共享资源项目的访问权限。 理想情况下,我将添加第二个项目以切换到共享资源项目的resx。

如何完成此操作? 有更好的方法吗?

alt text


Winforms,Web表单,版本? - John Saunders
WinForms -- .NET 3.5 -- VS 2008 - Douglas
7个回答

25

我能够在VS 2008 SP1中在项目之间共享resx文件,并在设计器中使用它(我不知道其他版本)。与其他方法不同,这里的资源是静态和动态共享的,因此在运行时实际上只有一个共享资源的副本。没有存储或内存浪费,并且很容易在大量项目中维护。

按照以下指南操作,然后告诉我它是否适用于您:

  • 创建一个类项目 Resources;
  • 创建一个资源文件(转到该项目的“属性”- > “资源”);
  • 将资源文件(默认为Resources.resx)移动到项目的根目录
  • 将资源文件重命名为GlobalResources.resx;
  • 将资源文件的修饰符修改为Public(双击资源文件,“访问修饰符”->“公共”);
  • 将任何资源文件复制到项目的根目录中;
  • 使用“添加现有文件”并选中项目根目录上的资源,向资源文件中添加任何资源。

在解决方案资源管理器中的项目应如下所示:

enter image description here

现在,在任何项目中,如果您需要共享resx文件,请按照以下步骤操作:

  • Add the "Resource" project as a dipendency;

  • Edit manually the project file (*.csproj) and add the following lines in the resources file ItemGroup (create a local resource file if you want to see it):

     <ItemGroup>
       ...
       <EmbeddedResource Include="..\Resources\GlobalResources.resx">
         <Link>GlobalResources.resx</Link>
         <Generator>ResXFileCodeGenerator</Generator>
         <LastGenOutput>GlobalResources.Designer.cs</LastGenOutput>
         <CustomToolNamespace>Resources</CustomToolNamespace>
       </EmbeddedResource>
       ...
     </ItemGroup>
    
很明显,“Include”属性应该是GlobalResources.resx的正确相对路径。
  • 像您所询问的那样在设计器中使用资源。
在我的项目中,当我这样做时,以下行会自动生成并自动添加到项目中:
<Compile Include="..\Resources\GlobalResources.Designer.cs">
  <Link>GlobalResources.Designer.cs</Link>
  <AutoGen>True</AutoGen>
  <DesignTime>True</DesignTime>
  <DependentUpon>GlobalResources.resx</DependentUpon>
</Compile>

如果它们没有被添加,需要手动添加。
现在项目在“解决方案资源管理器”中应该呈现出这样的结构(类似的文件应该有不同的标记):

enter image description here

最后两步:

  • 点击链接的"GlobalResources.resx",在属性中将"BuildAction"设置为"None"。"Custom Tool Namespace"应该已经设置为"Resources";

enter image description here

  • 点击链接的"GlobalResources.Designer.resx"并在属性中将"BuildAction"设置为"None"。

enter image description here

现在您已经完成了:此时,您应该真正能够在设计师中使用您添加到共享resx中的资源,选择问题的“Project Resource file”对话框中的“GlobalResources.resx”。并且如所述,它在运行时确实是共享的!在属性面板中,您应该看到外部项目的完整命名空间和类。如果您删除了对“Resource”项目的依赖关系,它甚至无法编译。如果它对您不起作用,请坚持直到它起作用,并在需要时告诉我指南哪里出错了。它必须工作!让我知道。


这个答案看起来几乎完美,我唯一关心的问题是CustomToolNamespace仍然相对于项目默认命名空间,不知道你是否知道一种方法来“覆盖”默认命名空间? - AussieALF
我不使用那个功能,抱歉。不确定它的有用性,你能举个例子吗?毕竟,当你拥有一个共享项目并且所有资源都动态地对整个解决方案可用时,你真的需要关心命名空间吗? - ceztko
将BuildAction设置为None会产生什么作用? - tofutim
它使得GlobalResources.resx不会在每个使用它的项目上编译,而是将其保留在一个共享程序集中的单个实例。 - ceztko
1
看起来设计师玩得很嗨皮。当我通过设计师在引用项目中添加图像时,它会更改名称空间和公共修饰符。因此,在MyProject.MyApp.App(具有表单的exe项目)中,在设计师中将图像添加到图片框中,而MyProject.MyApp.UI中的GlobalResources资源名称空间会更改为MyProject.MyApp.App。真是太好了! - Andez
显示剩余3条评论

9
最近我也遇到了同样的问题。查看了微软的文档,但是没有找到好的解决方案。最终,我通过下面描述的方法让WinForms设计器中的图像编辑器识别其他项目中的资源文件。这种方法很巧妙——如果有人有更简洁的解决方案,请发表。我只在VS 2008上进行了测试。
在开始之前,我应该说,如果你只想让Resources.resx文件中的项对其他项目可用,你可以编辑它的属性,并将生成的类的访问修饰符设置为public,这样它就使用PublicResXFileCodeGenerator。下面的操作仅在你想要像Douglas在上面的帖子中描述的那样使WinForms设计器拾取此资源文件中的图像时才需要。
假设你的解决方案具有以下布局。各个项目的默认命名空间(在解决方案资源管理器中单击项目节点,然后右键单击->属性->应用程序选项卡)在这里非常重要,因此我已经将其放在了项目名称后面的括号中。
- MySolution - SharedProject (默认命名空间=SharedProject) - SharedProject.csproj - WinFormsProject1 (默认命名空间=WinFormsProject1) - WinFormsProject1.csproj - WinFormsProject2 (默认命名空间=WinFormsProject2) - WinFormsProject2.csproj
你想在SharedProject中创建一个资源文件,其中包含可以被WinFormsProject1和WinFormsProject2使用的图像。
首先,在SharedProject的根目录中创建一个新的资源文件,我们称之为SharedResources.resx。这个文件使用ResXFileCodeGenerator而不是PublicResXFileCodeGenerator非常重要,原因将在后面变得清晰明了。
在Visual Studio中编辑SharedResources.resx并添加你想要共享的图像资源。Visual Studio还应该在SharedProject中为你生成一个名为SharedResources.designer.cs的类文件。
现在,你的解决方案应该如下所示:
- MySolution - SharedProject (默认命名空间=SharedProject) - SharedProject.csproj - SharedResources.resx - SharedResources.Designer.cs - WinFormsProject1 (默认命名空间=WinFormsProject1) - WinFormsProject1.csproj - WinFormsProject2 (默认命名空间=WinFormsProject2) - WinFormsProject2.csproj
如果你用文本编辑器打开SharedProject.csproj,你应该看到以下条目:
<EmbeddedResource Include="SharedResources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>SharedResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Include="SharedResources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>SharedResources.resx</DependentUpon>
</Compile>

现在您需要使资源在WinFormsProject1中可访问。为此,您需要在文本编辑器中编辑WinFormsProject1.csproj,因为您需要设置的某些属性未在Visual Studio中公开。

打开WinFormsProject1.csproj文件,并在ItemGroup中添加以下内容:

<Compile Include="..\SharedProject\SharedResources.Designer.cs">
    <Link>SharedResources.Designer.cs</Link>
    <AutoGen>True</AutoGen>
    <DesignTime>True</DesignTime>
    <DependentUpon>SharedResources.resx</DependentUpon>
</Compile>
<EmbeddedResource Include="..\SharedProject\SharedResources.resx">
    <Link>SharedResources.resx</Link>
    <Generator>ResXFileCodeGenerator</Generator>
    <LastGenOutput>SharedResources.Designer.cs</LastGenOutput>
    <CustomToolNamespace>SharedProject</CustomToolNamespace>
    <LogicalName>SharedProject.SharedResources.resources</LogicalName>
</EmbeddedResource>

重点是CustomToolNamespace和LogicalName。默认情况下,Visual Studio使用项目的默认命名空间来命名资源并生成*. Designer.cs文件。由于SharedProject和WinFormsProject1具有不同的默认命名空间,因此默认行为会导致嵌入在WinFormsProject1中的资源与SharedProject内部的*. Designer.cs文件不兼容。可以通过覆盖CustomToolNamespace和LogicalName来预防这种情况。
现在对WinFormsProject2.csproj执行相同操作。返回Visual Studio。它会注意到您已更改csproj文件并要求重新加载项目-选择“重新加载”。您应该发现在设计WinFormsProject1和WinFormsProject2中的表单时,现在可以从SharedResources.resx选择图像。
本质上,所有这些都是使VS包括SharedProject中的SharedResources.resx文件在WinFormsProject1和WinFormsProject2项目中。只有一个.resx源文件(在SharedProject中),但编译解决方案时实际上将有三个完全相同的类,称为SharedProject.SharedResources,在三个程序集中各有一个,但由于所有内部可见性,即使在项目之间添加引用也不会互相干扰。

我不知道你是如何找出这个问题的,但它在我的VS 2010中完美地运行了!!!! - Hasani Blackwell
这个解决方案有一个注意事项,你必须记住,你不是依赖于"SharedResource"库,而是将这些资源直接编译到你的应用程序中。 - AussieALF
使用这种方法,如果您有一个应用程序和两个依赖库需要访问SharedResources,则最终会得到3份资源副本。因此,我认为最好的方法是将SharedResources作为“模板”资源库来使用,以便在您的应用程序中“包含”,而不是从中依赖于您的项目的SharedResource。 - AussieALF

7

在VS2012中共享项目资源

只需3个简单步骤,仅使用设计师!

  1. 像往常一样创建项目并添加要共享的资源
  2. 创建另一个项目,并在“添加现有项”对话框中以“链接方式添加”第一个项目的resources.resx文件,该对话框可从上下文菜单中访问
  3. 根据需要从链接的资源文件中选择图像;这可以在“选择资源”对话框中的“项目资源文件”下拉菜单中选择,该对话框可从属性窗格中访问

完成!


那样做可以,但是在引用其他项目(引用资源类库项目)中的代码中添加图像后,请将图像添加到资源项目中的GlobalResources中。然后zut alores! - Andez
似乎当我使用设计器向引用项目中的图片框添加图像时出现了问题。 - Andez
这是唯一有效的解决方案,但我需要将访问修饰符更改为public。它位于资源设计编辑器中,“删除资源”右侧。 - user1695758

2

针对Visual Studio 2015及以上版本

有一种简单直接的方法,但该方法不受VisualStudio UI支持。因此,如果您不怕编辑项目文件,请继续阅读。

如果尚未这样做,请将一个新的共享项目添加到解决方案中。添加新的共享项目

如果尚未这样做,请将新的共享项目作为共享项目引用包含在外围项目中。输入图像说明

请注意,这个共享项目被以简单明了的方式包含在外围项目(.csproj文件)中:

<Import Project="..\Common\Common.projitems" Label="Shared" />

因此,即使VS UI没有显示它,共享项目( .projitems 文件)中的所有内容都直接包含在外围项目中。将以下资源定义添加到共享项目( .projitems 文件)中将直接将它们包含到您的外围项目中。但是,由于VS UI不支持共享项目的 Resource 元素,这些文件将在IDE中被隐藏和忽略。
<ItemGroup>
  <Resource Include="$(MSBuildThisFileDirectory)Resources\one 16x16.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\one 64x64.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\one.ico" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\two 16x16.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\three 16x16.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\four 16x16.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\five 16x16.png" />
  <Resource Include="$(MSBuildThisFileDirectory)Resources\six 16x16.png" />
</ItemGroup>

最后,你的外围项目资源可能具有相对路径(例如".\Resources\main.ico"),但是这些资源被编译到你的外围项目中时使用的是基础id,而不是绝对或相对路径。例如:
<Window x:Class="Common.Shared"
  ...
  Icon="one.ico">
  <Window.Resources>
    <ResourceDictionary>
      <BitmapImage x:Key="one64" UriSource="one 64x64.png"/>
    </ResourceDictionary>
  </Window.Resources>

这在一定程度上对我有帮助,但是如何在共享项目中包含的文件中引用字符串资源呢? - David

1
您只需要添加一个带有RESX文件的新类项目,其中包含资源(图像、文本等)。然后在其他项目中添加对此的引用,并使用Resource类名称以访问所需的资源,例如:Project.SharedImages.Upload_64x64(结果是一个位图,可以在这种情况下立即使用)。

当例如设置控件的图标时,资源在设计器中将不可用。 - IntegerWolf

0

Charlie的回答非常好用!

只需要做一个小改变:如果你想跳过最后两个步骤,在修改项目文件时请使用这个版本的包含项。

<None Include="..\Resources\GlobalResources.resx">
      <Link>GlobalResources.resx</Link>
      <Generator>PublicResXFileCodeGenerator</Generator>
      <LastGenOutput>GlobalResources.Designer.cs</LastGenOutput>
      <CustomToolNamespace>Resources</CustomToolNamespace>
</None>
<None Include="..\Resources\GlobalResources.Designer.cs">
      <Link>GlobalResources.Designer.cs</Link>
      <DependentUpon>GlobalResources.resx</DependentUpon>
</None>

生成器必须被设置为一些无效的值,比如“none”。请参考我的回答。 - IntegerWolf

0

对于 SDK 样式的项目,使用以下方法,或复制 .targets 文件的内容(未经测试)用于非 SDK 样式的项目。

1:在解决方案旁边创建以下文件。将占位符替换为全局资源的正确信息。例如,将其命名为“IncludeGlobalResources.targets”。

<Project>
  <!--Makes it possible to select the Images of My.Custom.Namespace.MyGlobalResourceProject in your Projects WindowsForms-Designers-->
  <!--See https://dev59.com/L3NA5IYBdhLWcg3wEpkU
  <ItemGroup>
    <None Include="..\My.Namespace.MyGlobalResourceProject\MyGlobalImages.resx">
      <Link>MyGlobalImages.resx</Link>
      <LastGenOutput>MyGlobalImages.Designer.cs</LastGenOutput>
      <Generator>none</Generator> <!-- Without this line you will get bugs as the referencing project will re-generate the source file with wrong namespaces -->
    </None>
  </ItemGroup>
  <ItemGroup>
    <None Include="..\My.Namespace.MyGlobalResourceProject\MyGlobalImages.Designer.cs">
      <Link>MyGlobalImages.Designer.cs</Link>
      <DependentUpon>MyGlobalImages.resx</DependentUpon>
    </None>
  </ItemGroup>
</Project>

2:转到项目文件(我使用的是SDK样式项目),您想要使用全局资源的位置。引用已创建的文件:

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="$(SolutionDir)\IncludeGlobalResources.targets" />
(...)

3:现在你只需将2.添加到你想要使用全局资源的每个项目中。此外,你还可以轻松扩展该文件以包含其他全局资源。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接