3

I have an application for which I use WPF. The application is dependent on a few libraries which I load as embedded resources. Mostly, this works like a charm.

Things go wrong when I try to add my own class library as an embedded resource as well (I'd like to keep the executable standalone). The library still loads and I am able to use all classes and controls it contains. What I am unable to do, however, is to define styles for these controls in any resource dictionary (e.g. app.xaml).


When I try to do something like this:
App.xaml:

<ResourceDictionary x:Class=....
                    xmlns:library="clr-namespace:myNamespace;assembly=assemblyName">
    <Style x:Key="Test" TargetType="{x:Type library:myControl}" />
</ResourceDictionary>

Main.xaml:

<Window x:Class=....
        xmlns:library="clr-namespace:myNamespace;assembly=assemblyName">
    <library:myControl style="{staticresource Test}" />
</Window>

I get the following error:
InvalidOperationException: 'myControl' TargetType does not match type of element 'myControl'.

Why is this? Can I somehow make clear that in fact, both myControl are the same type?


There are a few things that do work, but arent viable options for me. One is setting the style directly in Main.xaml:

<Window x:Class=....
        xmlns:library="clr-namespace:myNamespace;assembly=assemblyName">
    <library:myControl>
        <library:myControl.Style>
            <Style TargetType="{x:Type library:myControl}" />
        <library:myControl.Style/>
    </library:myControl>
</Window>

which works fine. However, I have hundreds of such controls which all need to implement largely the same style. It wouldn't want to repeat the same style in every window or control including myControl.


Something else that works to my surprise is writing the library data to a file, and then loading it. e.g. if I do:

var assembly = Assembly.GetExecutingAssembly();
string libraryName = "assemblyName.dll";
string library = assembly.GetManifestResourceNames().Where(s => s.EndsWith(libraryName)).First();
using (Stream stream = assembly.GetManifestResourceStream(library))
using (FileStream FS = File.Create(fullPath)) 
    stream.CopyTo(FS);
Assembly.LoadFile(Path.Combine(Directory.GetCurrentDirectory(), libraryName));

all my problems are gone. The catch is that now the assembly's loaded and I cannot delete the file I've just created. I've tried bypassing this by loading it into a separate domain and other suggestions given in questions such as this one, but to no avail: I cannot delete the loaded assembly even after unloading the domain it's loaded in.

Pudding
  • 85
  • 5
  • Are you saying that `assemblyName` is not referenced in the project file? How is the `App.xaml` supposed to be compiled then? – mm8 May 19 '21 at 19:48
  • Of course `assemblyName` is being referenced in the project file. However, I've set the `copy local` flag to `False` (this seems to set `False` in the actual .csproj file). I could set this flag to true, but I specifically don't want to have the dll as a separate file. Instead I load it as an embedded resource, a practice often used as seen [here](https://stackoverflow.com/a/27891551/8171835), [here](https://stackoverflow.com/a/625115/8171835), and [here](https://stackoverflow.com/a/15367634/8171835) – Pudding May 20 '21 at 08:03
  • So you are both referencing it and load it dynamically...? When do you load it then? Are you loading the exact same version as the referenced one? – mm8 May 20 '21 at 13:00
  • Yes, I've added the library as a reference with the `copy local` flag set to `false`. Instead of loading the DLL file from the working directory, however, I'm loading it as an embedded resource. I can confirm that the resource I'm loading is exactly the same as the referenced library. – Pudding May 25 '21 at 07:37

1 Answers1

0

Upon inspecting the target types of my styles (Style.TargetType) and the types of the controls (control.GetType()) in my wpf window, I found a few differences:

  • The RuntimeType.Assembly.CodeBase is different when loading the library as an embedded resource: assemblyName.dll for the type of the controls, and application.exe for the TargetType of the styles.
  • A few pointers also differ between the types: RuntimeType.Assembly.m_assembly and MemberInfo.Module.m_pData. This implies to me that the library is actually loaded twice in differing parts of memory, hence it can't be guaranteed that both types are the same.

This explains why writing the assembly to a file, and then loading it would solve the issue: both types then have Assembly.CodeBase pointing to assemblyName.dll and the pointers also nicely match.


Ultimately I found the following workaround:

By reloading the App.xaml ResourceDictionary in Window.Resources (MainWindow.xaml), the types in the ResourceDictionary are guaranteed to be the same as the type in the controls and everything works accordingly. This causes no issues for me as my application only has a single window, although it does seem a little sloppy to be loading the same ResourceDictionary twice (once application-wide, once in the scope of the window). The real question remains why the assembly codebase is different when loading app.xaml than it is for the rest of the application's lifespan.

Pudding
  • 85
  • 5
  • I think you have the solution of my problem. What do you mean by `reloading the app.xaml`? – Orace Oct 07 '22 at 08:04
  • Hey there, sorry to get back to you so late. Unfortunately my memory's a bit foggy on what I meant with reloading the `app.xaml`, and I no longer have access to my old codebase. I wish you the best of luck! – Pudding May 03 '23 at 16:18