Search code examples
c#avaloniauiavalonia

How do i get a ResourceDictionary from a xaml file in a dynamically loaded DLL


I have a DLL which contains a custom Avalonia control and its default style and resources.

I can load the DLL dynamically like this and use the control without issue:

var basePath = Directory.GetCurrentDirectory();
var dll = Assembly.LoadFile(Path.Combine(basePath, "Components.dll"));

foreach (var type in dll.GetExportedTypes())
{
  if (type.IsAssignableTo(typeof(IComponentPlugin)))
  {
    var instance = Activator.CreateInstance(type);
    if (instance is IComponentPlugin plugin)
    {
      // Do Stuff
    }
  }
}

Now, i want to add the default style and resources to my Application's, and it's somehow very hard. I can retrieve a StyleInclude and ResourceInclude from my DLL, by putting this in IComponentPlugin:

public ResourceInclude? GetResources()
{
    var uri = new Uri("avares://DefaultResources.axaml");
    return new ResourceInclude(uri) { Source = uri };
}

public StyleInclude? GetStyle()
{
    var uri = new Uri("avares://DefaultStyle.axaml");
    return new StyleInclude(uri) { Source = uri };
}

But, when i try to add it to my Application's resources, AvaloniaXamlLoader complains. "No precompiled XAML found for avares://DefaultResources.axaml/ (baseUri: avares://DefaultResources.axaml/)".

I tried instead getting the content of the file and loading it with AvaloniaRuntimeXamlLoader from the Avalonia.Markup.Xaml.Loader package:

public Stream? GetResources()
{
    return AssetLoader.Open(new Uri("avares://DefaultResources.axaml"));
}

But then, i get a FileNotFoundException "The resource avares://DefaultResources.axaml/ could not be found.". I also tried with "avares://Components/DefaultResources.axaml" to no avail.

What would be the way to add the resources to my Application ?


Solution

  • I managed to find a workaround by setting DefaultResources.axaml and DefaultStyles.axaml to EmbeddedResources, and the following code:

    foreach (var resourceName in dll.GetManifestResourceNames())
    {
      if (resourceName.EndsWith("DefaultResources.axaml"))
      {
        var resourceStream = dll.GetManifestResourceStream(resourceName);
        if (resourceStream != null)
        {
          var resource = AvaloniaRuntimeXamlLoader.Load(resourceStream, dll);
          if (resource is IResourceProvider resourceProvider)
            Application.Current.Resources.MergedDictionaries.Add(resourceProvider);
        }
      }
      // Same for DefaultStyle.axaml
    }
    

    It would be more convenient to be able to use AvaloniaResources, though.