如何从WPF控件中获取静态资源的资源键?
如何获取WPF控件绑定的静态资源键?
在WPF中使用StaticResource绑定资源时,运行时通过ReadLocalValue只能获取到资源的实际值(比如示例中的"Will"),无法直接拿到对应的资源键(比如"MyName")。我们需要在Debug模式下获取这个资源键,将其设置为TextBlock的ToolTip,方便验证是否使用了正确的资源。
相关代码示例
XAML代码
<Window x:Class="GetResourceKeyWpf.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=System.Runtime" Width="800" Height="450" WindowStartupLocation="CenterScreen"> <Window.Resources> <system:String x:Key="MyName">Will</system:String> </Window.Resources> <StackPanel> <TextBlock Loaded="MyNameTb_Loaded" Text="{StaticResource MyName}" /> </StackPanel> </Window>
C#代码
using System.Windows; namespace GetResourceKeyWpf; public sealed partial class MainWindow { public MainWindow() { InitializeComponent(); } private void MyNameTb_Loaded(object sender, RoutedEventArgs e) { // 如何获取Text属性对应的静态资源键"MyName"? } }
实现方案
StaticResource是在XAML编译阶段就被解析为实际值的,运行时控件属性直接存储资源值,不会保留资源键信息。要获取资源键,可通过以下两种方式实现:
方案一:解析BAML获取静态资源标记信息
通过读取控件对应的BAML流,解析其中的StaticResourceExtension标记来提取资源键。需要引用System.Xaml程序集,代码示例如下:
using System.IO; using System.Windows; using System.Windows.Markup; using System.Xaml; namespace GetResourceKeyWpf; public sealed partial class MainWindow { public MainWindow() { InitializeComponent(); } private void MyNameTb_Loaded(object sender, RoutedEventArgs e) { #if DEBUG if (sender is TextBlock tb) { var resourceKey = GetStaticResourceKeyForProperty(tb, TextBlock.TextProperty); tb.ToolTip = resourceKey; } #endif } private string? GetStaticResourceKeyForProperty(DependencyObject obj, DependencyProperty dp) { // 获取控件对应的BAML资源流 var assembly = obj.GetType().Assembly; var bamlName = $"{obj.GetType().FullName}.baml"; using var stream = assembly.GetManifestResourceStream(bamlName); if (stream == null) return null; // 解析BAML内容 var reader = new Baml2006Reader(stream); while (reader.Read()) { if (reader.NodeType == XamlNodeType.StartObject && reader.Type == obj.GetType()) { // 遍历控件的属性节点 while (reader.Read() && reader.NodeType != XamlNodeType.EndObject) { if (reader.NodeType == XamlNodeType.StartMember && reader.Member.Name == dp.Name) { // 读取属性的标记扩展 if (reader.Read() && reader.NodeType == XamlNodeType.StartObject && reader.Type == typeof(StaticResourceExtension)) { // 提取ResourceKey值 while (reader.Read() && reader.NodeType != XamlNodeType.EndObject) { if (reader.NodeType == XamlNodeType.StartMember && reader.Member.Name == nameof(StaticResourceExtension.ResourceKey)) { if (reader.Read() && reader.NodeType == XamlNodeType.Value) { return reader.Value.ToString(); } } } } } } break; } } return null; } }
方案二:自定义StaticResource扩展(更简洁)
自定义一个继承自StaticResourceExtension的扩展类,在Debug模式下将资源键附加到目标控件的附加属性中,直接读取即可。
- 自定义扩展类与附加属性:
using System.Windows; using System.Windows.Markup; namespace GetResourceKeyWpf; [MarkupExtensionReturnType(typeof(object))] public class DebugStaticResourceExtension : StaticResourceExtension { public DebugStaticResourceExtension() { } public DebugStaticResourceExtension(object resourceKey) : base(resourceKey) { } public override object ProvideValue(IServiceProvider serviceProvider) { var value = base.ProvideValue(serviceProvider); #if DEBUG // 获取目标控件与属性 if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget target) { if (target.TargetObject is DependencyObject obj && target.TargetProperty is DependencyProperty dp) { // 将资源键存入附加属性 obj.SetValue(ResourceKeyTracker.ResourceKeyProperty, ResourceKey); } } #endif return value; } } // 定义附加属性存储资源键 public static class ResourceKeyTracker { public static readonly DependencyProperty ResourceKeyProperty = DependencyProperty.RegisterAttached("ResourceKey", typeof(object), typeof(ResourceKeyTracker)); public static void SetResourceKey(DependencyObject obj, object value) { obj.SetValue(ResourceKeyProperty, value); } public static object GetResourceKey(DependencyObject obj) { return obj.GetValue(ResourceKeyProperty); } }
- 修改XAML中的资源引用:
<!-- 先添加local命名空间引用 xmlns:local="clr-namespace:GetResourceKeyWpf" --> <TextBlock Loaded="MyNameTb_Loaded" Text="{local:DebugStaticResource MyName}" />
- 在Loaded事件中获取资源键:
private void MyNameTb_Loaded(object sender, RoutedEventArgs e) { #if DEBUG if (sender is TextBlock tb) { var resourceKey = ResourceKeyTracker.GetResourceKey(tb); tb.ToolTip = resourceKey.ToString(); } #endif }
方案说明
- 方案一无需修改现有XAML,但解析BAML逻辑较复杂,仅适用于当前程序集内的控件。
- 方案二更简洁通用,仅需替换原
StaticResource为自定义扩展,且仅在Debug模式下生效,不影响Release版本性能。
内容的提问来源于stack exchange,提问作者user6231009
相关产品推荐
相关产品推荐

