UWP中ResourceDictionary自定义控件的SvgImageSource加载失败问题
问题详情
正在将WinUI3库移植到UWP,自定义了public sealed class CustomButton : Button控件,包含Icon属性(类型为ImageSource)。控件UI通过ResourceDictionary定义,在App.xaml的MergedDictionaries中导入:
<CustomDictionary xmlns="using:CustomLibrary.UWP.UI" />
需求是为不同CustomButton样式设置不同SVG图标,同时允许用户自定义图标。在WinUI3中,会在ResourceDictionary的代码后置里通过以下代码将SVG转为SvgImageSource(因ResourceDictionary无法使用x:Bind):
((Style)this["DefaultCustomButtonStyle"]).Setters.Add(new Setter(CustomButton.IconProperty, Icons.ToImageSource(Icons.Default)));
但在UWP中,图片显示空白——调试发现图片已创建且尺寸正确,但内容为空。Icons.ToImageSource函数单独测试正常,直接给Image的Source赋值能正常显示SVG。
奇怪的是:在App.xaml中导入字典就会出问题,在使用CustomButton的视图内导入字典则一切正常。
原因分析
UWP中App.xaml的ResourceDictionary加载时机早于控件初始化,此时SvgImageSource的异步加载逻辑可能还未完成,或者资源上下文存在差异,导致SVG内容无法正确渲染到Style的Setter中。而在视图内导入字典时,视图加载时机更晚,控件上下文已就绪,SVG加载逻辑能正常执行。
解决方案
方案1:延迟图标赋值到控件加载时
不在ResourceDictionary的构造函数或Loaded事件中直接设置Setter,而是在CustomButton的首次加载事件中处理图标赋值,仅当用户未自定义Icon且使用默认样式时自动赋值:
public sealed class CustomButton : Button { public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(ImageSource), typeof(CustomButton), new PropertyMetadata(null)); public ImageSource Icon { get => (ImageSource)GetValue(IconProperty); set => SetValue(IconProperty, value); } protected override void OnLoaded(object sender, RoutedEventArgs e) { base.OnLoaded(sender, e); // 仅当未自定义Icon且使用默认样式时赋值 if (Icon == null && Style?.Key is string key && key == "DefaultCustomButtonStyle") { Icon = Icons.ToImageSource(Icons.Default); } } }
方案2:在ResourceDictionary模板应用后设置Setter
若坚持在ResourceDictionary中处理,可重写OnApplyTemplate方法,确保控件模板应用完成后再添加Setter:
public sealed partial class CustomDictionary : ResourceDictionary { protected override void OnApplyTemplate() { base.OnApplyTemplate(); if (TryGetValue("DefaultCustomButtonStyle", out object styleObj) && styleObj is Style style) { style.Setters.Add(new Setter(CustomButton.IconProperty, Icons.ToImageSource(Icons.Default))); } } }
方案3:直接引用嵌入式SVG资源
把SVG文件作为嵌入式资源,在XAML中直接通过SvgImageSource的Uri引用,绕开代码后置的时机问题:
<Style x:Key="DefaultCustomButtonStyle" TargetType="local:CustomButton"> <Setter Property="Icon" Value="ms-appx:///CustomLibrary.UWP/Assets/DefaultIcon.svg" /> </Style>
内容的提问来源于stack exchange,提问作者IcySnex

