You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现内部NuGet包中WPF资源合并到应用的Application.Resources.MergedDictionary?

自动合并WPF资源到消费应用的NuGet方案

我刚好处理过类似的场景,给你一套完整的实现方案,能让你的WPF资源(字体、样式这类)从NuGet包自动合并到消费应用的Application.Resources.MergedDictionaries里,完全不用手动操作:

1. 搭建NuGet包的核心结构

首先得把资源和自动合并的逻辑打包到NuGet里,结构大概是这样:

  • lib/net6.0-windows/:放包含初始化逻辑的类库(如果用代码动态加载的话)
  • contentFiles/any/net6.0-windows/WpfResources/:放你的自定义字体、样式XAML文件(比如CustomFonts.xamlButtonStyles.xaml
  • build/net6.0-windows/:放MSBuild目标文件,负责自动注入资源引用

2. 选择自动合并的两种方式

方式一:通过MSBuild修改App.xaml(无需代码侵入)

这种方式会在编译时自动把资源字典引用写入到App.xamlMergedDictionaries中,用户完全看不到修改过程。

创建一个WpfResourceMerger.targets文件,内容如下:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- 确保只在WPF项目中执行 -->
  <PropertyGroup>
    <IsWpfProject>$(MSBuildProjectExtension.Contains('.csproj')) And $(UseWPF)</IsWpfProject>
  </PropertyGroup>

  <Target Name="MergeWpfResources" AfterTargets="Build" Condition="$(IsWpfProject)">
    <!-- 检查App.xaml是否存在 -->
    <ItemGroup>
      <AppXamlFile Include="$(ProjectDir)App.xaml" Condition="Exists('$(ProjectDir)App.xaml')" />
    </ItemGroup>

    <!-- 注入字体资源字典 -->
    <XmlPoke 
      XmlInputPath="@(AppXamlFile)"
      Query="/Application/Resources/MergedDictionaries/ResourceDictionary[@Source='pack://application:,,,/YourNuGetPackageId;component/WpfResources/CustomFonts.xaml']"
      Value=""
      Condition="@(AppXamlFile) != ''" />

    <XmlPoke 
      XmlInputPath="@(AppXamlFile)"
      Query="/Application/Resources"
      Value="&lt;MergedDictionaries&gt;&lt;ResourceDictionary Source=&quot;pack://application:,,,/YourNuGetPackageId;component/WpfResources/CustomFonts.xaml&quot; /&gt;&lt;/MergedDictionaries&gt;"
      Condition="@(AppXamlFile) != '' and !Exists('/Application/Resources/MergedDictionaries/ResourceDictionary[@Source='pack://application:,,,/YourNuGetPackageId;component/WpfResources/CustomFonts.xaml']')" />

    <!-- 可以复制上面的XmlPoke块,添加更多资源文件(比如样式XAML) -->
  </Target>
</Project>

注意把YourNuGetPackageId替换成你实际的NuGet包ID,每个资源文件都要对应一个XmlPoke块。

方式二:代码动态加载(更灵活,支持多框架)

如果不想修改App.xaml,可以用代码在应用启动时自动添加资源字典,适合需要动态判断加载的场景。

  1. 在NuGet的类库中创建初始化类:
using System.Windows;
using System.Linq;

namespace YourNuGetNamespace
{
    public static class WpfResourceLoader
    {
        public static void LoadAllResources()
        {
            // 加载字体资源
            LoadResourceDictionary(new Uri("pack://application:,,,/YourNuGetPackageId;component/WpfResources/CustomFonts.xaml"));
            // 加载样式资源
            LoadResourceDictionary(new Uri("pack://application:,,,/YourNuGetPackageId;component/WpfResources/ButtonStyles.xaml"));
        }

        private static void LoadResourceDictionary(Uri resourceUri)
        {
            if (Application.Current == null) return;

            var dictionary = new ResourceDictionary { Source = resourceUri };
            // 避免重复添加
            if (!Application.Current.Resources.MergedDictionaries.Any(d => d.Source?.Equals(resourceUri) ?? false))
            {
                Application.Current.Resources.MergedDictionaries.Add(dictionary);
            }
        }
    }
}
  1. 用ModuleInitializer自动执行(.NET 5+支持):
using System.Runtime.CompilerServices;

namespace YourNuGetNamespace
{
    internal static class ResourceInitializer
    {
        [ModuleInitializer]
        public static void Initialize()
        {
            WpfResourceLoader.LoadAllResources();
        }
    }
}

这样应用启动时会自动执行初始化,完全不需要消费端写任何代码。

3. 打包NuGet的关键配置

.nuspec文件里要确保所有文件都被正确包含:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  <metadata>
    <id>YourNuGetPackageId</id>
    <version>1.0.0</version>
    <title>WPF Custom Resources</title>
    <authors>Your Team</authors>
    <description>Auto-merging WPF fonts and styles via NuGet</description>
    <frameworks>
      <framework target="net6.0-windows" />
      <!-- 添加你需要支持的其他框架,比如net48 -->
    </frameworks>
  </metadata>
  <files>
    <!-- 类库文件 -->
    <file src="bin\Release\net6.0-windows\YourNuGetNamespace.dll" target="lib/net6.0-windows" />
    <!-- 资源文件 -->
    <file src="WpfResources\*.xaml" target="contentFiles/any/net6.0-windows/WpfResources" />
    <!-- MSBuild目标文件 -->
    <file src="build\WpfResourceMerger.targets" target="build/net6.0-windows" />
  </files>
</package>

4. 测试验证

安装NuGet包到你的WPF应用后:

  • 如果用方式一:打开App.xaml,会看到自动添加了ResourceDictionary引用
  • 如果用方式二:直接运行应用,自定义字体和样式会自动生效,无需任何手动配置

内容的提问来源于stack exchange,提问作者TheGeneral

火山引擎 最新活动