Home > Blockchain >  Access Themes/Generic.xaml via reflection
Access Themes/Generic.xaml via reflection

Time:12-07

Is there any way to access the Themes/Generic ResourceDictionary using reflection?

I tried digging through Application.Resources but this appears to be the wrong place. Then I tried analysing the Assembly's ResourceStream but that wasn't succesful either.

So does anyone know how I can get the instance of the mentioned ResourceDictionary from an assembly?

Before someone asks "Why" => because i want to mess with it.

This is the closest i got, but it throws errors that some parts of it cannot be loaded, so I assume its a "copy" and not the one used?

        System.Uri resourceLocater = new System.Uri("/ASMNAME;component/themes/generic.xaml", System.UriKind.Relative);

        ResourceDictionary dictionary =(ResourceDictionary) Application.LoadComponent(resourceLocater);

CodePudding user response:

The provided solution of @CSharpie is slow as it uses reflection in order to invoke internal library code. But more important, its implementation is very unsafe and very unstable as it relies on non-public library code that can change at any time. Using such implementations in production code is irresponsible and unprofessional.
In case of the referenced answer, instead of using reflection to invoke the internals of FrameworkElement.FindResource simply invoke this method directly!

Better use the public API:

Generic.xaml

<ResourceDictionary>
  <Style TargetType="{x:Type MyCustomControl}">
    ...
  </Style>
</ResourceDictionary>

The following code assumes that the Generic.xaml file is located in an assembly named "Net.Wpf". You can call Assembly.GetManifestResourceNames to get a list of existing resource names.
In the example, the required name is "Net.Wpf.g.resources".
Then lookup the resource like this:


// Get the assembly that contains the Themes/Generic.xaml file
Assembly themesAssembly = Assembly.GetExecutingAssembly();
await using Stream themesResourceStream = themesAssembly.GetManifestResourceStream("Net.Wpf.g.resources");
using var resourceReader = new System.Resources.ResourceReader(themesResourceStream);
await using var themesBamlStream = resourceReader
  .OfType<DictionaryEntry>()
  .First(entry => entry.Key.Equals("themes/generic.baml"))
  .Value as Stream;
using var themesBamlReader = new System.Windows.Baml2006.Baml2006Reader(themesBamlStream);
var genericXamlResources = System.Windows.Markup.XamlReader.Load(themesBamlReader) as ResourceDictionary;
Style customControlStyle = genericXamlResources[typeof(MyCustomControl)] as Style;

// *******************************
// Same can be achieved by calling
Style customControlStyle = FindResource(typeof(ShortcutButton)) as Style;

Note, the XAML resource dictionaries (with build action set to "Page") are usually compiled to BAML (a binary format) before embedded into the assembly. You therefore must convert from BAML back to XAML before you can access the resources.

CodePudding user response:

I got it working now

I found how to do it here: https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/SystemResources.cs,376

Assembly asm = .....
var systemResourcesType = typeof(Application).Assembly.GetType("System.Windows.SystemResources");
var ensuremethod = systemResourcesType.GetMethod("EnsureDictionarySlot", BindingFlags.NonPublic | BindingFlags.Static);
var loadMethod = ensuremethod.ReturnType.GetMethod("LoadGenericDictionary", BindingFlags.NonPublic | BindingFlags.Instance);

var resourceDictionaries = ensuremethod.Invoke(null, new[] { asm });

var resourceDictionary = (ResourceDictionary)loadMethod.Invoke(resourceDictionaries, new object[]{false});
  • Related