原文 https://*.com/questions/28240528/how-do-i-duplicate-a-resource-reference-in-code-behind-in-wpf
如何在WPF后台代码中中复制引用的资源?
In my application, I have a color resources. I have one element that uses that color as a dynamic resource in xaml. <Window x:Class="ResourcePlay.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="425"> <Window.Resources> <Color x:Key="MyColor">Red</Color> </Window.Resources> <Grid> <Rectangle VerticalAlignment="Top" Width="80" Height="80" Margin="10"> <Rectangle.Fill> <SolidColorBrush x:Name="TopBrush" Color="{DynamicResource MyColor}"/> </Rectangle.Fill> </Rectangle> <Rectangle VerticalAlignment="Bottom" Width="80" Height="80" Margin="10"> <Rectangle.Fill> <SolidColorBrush x:Name="BottomBrush"/> </Rectangle.Fill> </Rectangle> </Grid> </Window> In the code, I want to duplicate this resource reference. using System.Windows; using System.Windows.Media; namespace ResourcePlay { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // I want to copy the resource reference, not the color. BottomBrush.Color = TopBrush.Color; // I'd really rather do something like this. var reference = TopBrush.GetResourceReference(SolidColorBrush.ColorProperty); BottomBrush.SetResourceReference(reference); // I want this to change the colors of both elements Resources["MyColor"] = Colors.Green; } } } However, SetResourceReference only works for FrameworkElements or FrameworkContentElements. SolidColorBrush is just a Freezable. Also, I have no idea how to get a resource reference in code behind. Is there a way to do this in WPF so that both of the colors change at the same time? In my real application, the problem isn't quite so simple, so I can't just add a second DynamicResource in xaml.
Il Vic suggested using reflection. Expanding on that, I was able to build some extension methods for DependencyObject that do what I want. I don't really like using reflection in code, and if someone else knows a better way to implement this, I'd love to see it. At least this will be helpful whenever I'm trying to debug DynamicResources from code behind. public static class DependencyObjectExtensions { public static object GetDynamicResourceKey(this DependencyObject obj, DependencyProperty prop) { // get the value entry from the depencency object for the specified dependency property var dependencyObject = typeof(DependencyObject); var dependencyObject_LookupEntry = dependencyObject.GetMethod("LookupEntry", BindingFlags.NonPublic | BindingFlags.Instance); var entryIndex = dependencyObject_LookupEntry.Invoke(obj, new object[] { prop.GlobalIndex }); var effectiveValueEntry_GetValueEntry = dependencyObject.GetMethod("GetValueEntry", BindingFlags.NonPublic | BindingFlags.Instance); var valueEntry = effectiveValueEntry_GetValueEntry.Invoke(obj, new object[] { entryIndex, prop, null, 0x10 }); // look inside the value entry to find the ModifiedValue object var effectiveValueEntry = valueEntry.GetType(); var effectiveValueEntry_Value = effectiveValueEntry.GetProperty("Value", BindingFlags.Instance | BindingFlags.NonPublic); var effectiveValueEntry_Value_Getter = effectiveValueEntry_Value.GetGetMethod(nonPublic: true); var rawEntry = effectiveValueEntry_Value_Getter.Invoke(valueEntry, new object[0]); // look inside the ModifiedValue object to find the ResourceReference var modifiedValue = rawEntry.GetType(); var modifiedValue_BaseValue = modifiedValue.GetProperty("BaseValue", BindingFlags.Instance | BindingFlags.NonPublic); var modifiedValue_BaseValue_Getter = modifiedValue_BaseValue.GetGetMethod(nonPublic: true); var resourceReferenceValue = modifiedValue_BaseValue_Getter.Invoke(rawEntry, new object[0]); // check the ResourceReference for the original ResourceKey var resourceReference = resourceReferenceValue.GetType(); var resourceReference_resourceKey = resourceReference.GetField("_resourceKey", BindingFlags.NonPublic | BindingFlags.Instance); var resourceKey = resourceReference_resourceKey.GetValue(resourceReferenceValue); return resourceKey; } public static void SetDynamicResourceKey(this DependencyObject obj, DependencyProperty prop, object resourceKey) { var dynamicResource = new DynamicResourceExtension(resourceKey); var resourceReferenceExpression = dynamicResource.ProvideValue(null); obj.SetValue(prop, resourceReferenceExpression); } } The second method uses DynamicResourceExtension to avoid some nastiness with Activator, but the first method feels incredibly brittle. I can use these methods in my original example as follows: public MainWindow() { InitializeComponent(); var key = TopBrush.GetDynamicResourceKey(SolidColorBrush.ColorProperty); BottomBrush.SetDynamicResourceKey(SolidColorBrush.ColorProperty, key); Resources["MyColor"] = Colors.Green; } This will work for any DependencyProperty, provided it is set to a DynamicResource when we try to get the resource key. A little more finesse would be needed for production code.