You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置版本后,WPF无法加载版本化DLL中XAML资源的问题

问题背景

使用Visual Studio 2022 + .NET 6开发WPF应用,将XAML资源(控件、样式、颜色等)分离到Presentation.dll共享库中,通过MyStyles.xaml合并资源:

<ResourceDictionary.MergedDictionaries>
    <styles:SharedResourceDictionary Source="Presentation;component/Styles/ColorDefinitions.xaml"/>
    <styles:SharedResourceDictionary Source="Presentation;component/Styles/FontDefinitions.xaml"/>
</ResourceDictionary.MergedDictionaries>

在Application.Resources中引用:

<ResourceDictionary Source="/Presentation;component/Styles/MyStyles.xaml" />

应用项目引用Presentation项目,未设置版本前运行正常。通过Directory.Build.props为Presentation.dll设置版本后:

<Project>
    <PropertyGroup>
        <AssemblyVersion>1.0.0.0</AssemblyVersion>
        <Version>1.0.0.0</Version>
    </PropertyGroup>
</Project>

应用抛出异常:

System.Windows.Markup.XamlParseException
  HResult=0x80131501
  Message='Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception.' Line number '10' and line position '42'.
  Source=PresentationFramework
  StackTrace:
   at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri) in /_/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/XamlReader.cs:line 527

  This exception was originally thrown at this call stack:
    System.Windows.ResourceDictionary.SetDeferrableContent(System.Windows.DeferrableContent) in ResourceDictionary.cs
    System.Windows.Baml2006.WpfSharedBamlSchemaContext.Create_BamlProperty_ResourceDictionary_DeferrableContent.AnonymousMethod__297_0(object, object) in WpfGeneratedKnownProperties.cs
    System.Windows.Baml2006.WpfKnownMemberInvoker.SetValue(object, object) in WpfKnownMemberInvoker.cs
    MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(System.Xaml.XamlMember, object, object) in ClrObjectRuntime.cs
    MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(object, System.Xaml.XamlMember, object) in ClrObjectRuntime.cs

Inner Exception 1:
InvalidOperationException: Cannot re-initialize ResourceDictionary instance.

补充:跨解决方案引用已编译的版本化Presentation.dll,使用pack://application:,,,/Presentation;component/Styles/MyStyles.xaml可正常加载,但项目引用版本化Presentation时仍报错;应用规模大,使用SharedResourceDictionary避免x86环境内存不足。

问题原因

核心问题是项目引用模式下,WPF的资源加载机制在处理带版本的程序集时,会重复初始化SharedResourceDictionary实例。

当通过项目引用时,构建过程中WPF会尝试优化资源加载,而SharedResourceDictionary的设计是单例复用资源,版本号的引入改变了程序集的标识,导致加载逻辑冲突,触发Cannot re-initialize ResourceDictionary instance异常。

解决方案

针对项目引用场景,可通过以下方式修复:

1. 显式指定程序集版本的pack URI

在Application.Resources中引用时,补充程序集版本信息到pack URI:

<ResourceDictionary Source="pack://application:,,,/Presentation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null;component/Styles/MyStyles.xaml" />

同时,MyStyles.xaml中的合并字典也需更新为带版本的URI:

<ResourceDictionary.MergedDictionaries>
    <styles:SharedResourceDictionary Source="pack://application:,,,/Presentation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null;component/Styles/ColorDefinitions.xaml"/>
    <styles:SharedResourceDictionary Source="pack://application:,,,/Presentation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null;component/Styles/FontDefinitions.xaml"/>
</ResourceDictionary.MergedDictionaries>

如果后续版本变更,需同步更新URI中的版本号,或通过编译常量动态替换。

2. 修改SharedResourceDictionary的缓存键逻辑

默认SharedResourceDictionary通常以Source URI作为缓存键,带版本的URI会生成新键,导致重复加载。可修改SharedResourceDictionary的缓存逻辑,忽略版本号或使用程序集名称+资源路径作为缓存键:

public class SharedResourceDictionary : ResourceDictionary
{
    private static readonly Dictionary<string, ResourceDictionary> _cache = new();
    private Uri _source;

    public new Uri Source
    {
        get => _source;
        set
        {
            _source = value;
            var cacheKey = GetCacheKey(value);
            if (_cache.TryGetValue(cacheKey, out var dict))
            {
                MergedDictionaries.Clear();
                MergedDictionaries.Add(dict);
            }
            else
            {
                base.Source = value;
                _cache[cacheKey] = this;
            }
        }
    }

    private string GetCacheKey(Uri uri)
    {
        // 提取程序集名称和资源路径,忽略版本信息
        var uriString = uri.ToString();
        var componentIndex = uriString.IndexOf(";component/");
        if (componentIndex > 0)
        {
            var assemblyPart = uriString.Substring(0, componentIndex);
            var assemblyName = assemblyPart.Split(',').First().Trim();
            var resourcePath = uriString.Substring(componentIndex);
            return $"{assemblyName}{resourcePath}";
        }
        return uriString;
    }
}

此方式确保不同版本的同一资源使用同一个缓存键,避免重复初始化。

3. 禁用WPF项目的资源优化

在Presentation项目的.csproj中添加以下配置,禁用WPF的资源优化,避免加载逻辑冲突:

<PropertyGroup>
    <OptimizeResources>false</OptimizeResources>
</PropertyGroup>

该配置会关闭WPF对资源的编译优化,可能影响少量加载性能,但能解决版本导致的初始化冲突。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:13:12