AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
26.04k stars 2.25k forks source link

Can't load ResourceDictionary from source #17509

Closed rickx closed 4 days ago

rickx commented 1 week ago

Describe the bug

Hi,

I tried all possible variations and alternatives, but in the end, I think this is simply a bug. Loading Avalonia ResourceDictionary files from source files fails. And actually, I don't think it matters whether it's ResourceDictionaries or any other xaml files - it never reaches the "reading" of the files anyway.

The error is Avalonia.Markup.Xaml.XamlLoadException: 'No precompiled XAML found for avares://AvaResourceDictionary_Loading/Resources/Dictionary.en-EN.axaml (baseUri: ), make sure to specify x:Class and include your XAML file as AvaloniaResource'

so I think there are 2 possible reasons:

There is also an AvaloniaRuntimeXamlLoader but it's not accessible in 11.2.0 - maybe that would be the way of reading not-compiled xaml files(?)

To Reproduce

Here's a small test project:

{
    InitializeComponent();
    LoadResourceDictionary();
}
private void LoadResourceDictionary() { 
    var uri = new Uri("avares://AvaResourceDictionary_Loading/Resources/Dictionary.en-EN.axaml"); 
    var resourceDictionary = (ResourceDictionary)AvaloniaXamlLoader.Load(uri);
    Application.Current?.Resources.MergedDictionaries.Add(resourceDictionary); 
}

the resource file(s) may be empty - does not matter. Set to AvaloniaResource.

Expected behavior

I should be able to load ResourceDictionaries from source and merge them at will. I used such a technique in WPF to conditionally load language and style dictionaries.

Avalonia version

11.2.0

OS

Windows

Additional context

Also tried ResourceInclude with no joy. It uses the same AvaloniaXamlLoader.Load in the end.

rabbitism commented 1 week ago

AvaloniaRuntimeXamlLoader is in Avalonia.Markup.Xaml.Loader nuget package. but it only loads raw xaml file, not assets.

maxkatz6 commented 1 week ago

ResourceInclude is a typical API to load XAML dynamically. AvaloniaXamlLoader would also work, just lower level. Your problem is likely with trimming. Dynamic XAML loading can't work, if you app is trimmed (which it is by default since .NET 8+ I think).

If it's possible, you should always pre-include resources/styles in XAML, even as a keyed resource (i.e. not a merged resource includes, but just <ResourceInclude x:Key="PreincludedResources" Source="Resources.xaml" /> a resource entry, which then can be read in runtime).

timunie commented 1 week ago

PreincludedResources

Huh? Didn't know this is a thing. If it's not yet in the docs, it should be mentioned there.

maxkatz6 commented 1 week ago

Just like any other types, you can include any object in resources :) Including ResourceInclude.

rickx commented 1 week ago

could you elaborate on accessing them at runtime? So, I added them as keyed resources. And that btw works only from App.axaml, not other controls. Now at runtime I find them all nicely loaded under app.Resources. Which, for my goal, means I have way more than I need. So I would pick the needed ones from there, merge them with app.Resources.MergedDictionaries.Add(,,,) and at that point, I would remove the unneeded Resources. Is that right? Are there other/better ways?

maxkatz6 commented 6 days ago

@rickx just like with any other keyed resource, it doesn't have to be defined in App.xaml. It can be inside any other XAML control, or nested merged dictionary. But you are right about how to use it, MergedDictionaries.Add and MergedDictionaries.Remove in runtime, using ResourceInclude read from the resources themselves.

maxkatz6 commented 6 days ago

As an example, we use similar technique for Compact styles of FluentTheme: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Themes.Fluent/FluentTheme.xaml#L21-L22

https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs#L32