Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Application resources confusion

Open RomanSoloweow opened this issue 3 years ago • 11 comments

I am facing the problem of getting application resources. Described it in discussions: https://github.com/AvaloniaUI/Avalonia/discussions/6019

As a result, it turned out that the indexer only returns inner elements: https://github.com/AvaloniaUI/Avalonia/blob/7842883961d094e08e9def7f30cf32fd573179c7/src/Avalonia.Base/Collections/AvaloniaDictionary.cs#L72

Instead of viewing all dictionaries as implemented in the "TryGetResource" methods https://github.com/AvaloniaUI/Avalonia/blob/7842883961d094e08e9def7f30cf32fd573179c7/src/Avalonia.Styling/Controls/ResourceDictionary.cs#L104

This needs to be fixed to avoid questions and difficulties when using

RomanSoloweow avatar Jun 06 '21 15:06 RomanSoloweow

Doesn't sounds like a bug for me. Isn't WPF/UWP has the same behavior? Resources is a dictionary instance per control, and to lookup parent control it has separated method.

maxkatz6 avatar Jun 06 '21 15:06 maxkatz6

In WPF, there is no such problem and the work with application resources is direct.

Application.Current.Resources["ColorConnector"]

RomanSoloweow avatar Jun 06 '21 15:06 RomanSoloweow

Doesn't the same code work in Avalonia?

maxkatz6 avatar Jun 06 '21 15:06 maxkatz6

No, this is exactly what I described in the discussion https://github.com/AvaloniaUI/Avalonia/discussions/6019

RomanSoloweow avatar Jun 06 '21 15:06 RomanSoloweow

Any updates?

TheVeryStarlk avatar Aug 07 '22 02:08 TheVeryStarlk

@StarlkYT current behavior is pretty much by design. Resources property is a dictionary, where indexer works as it would be expected from dictionary. It's possible to change it to match WPF behavior though. @grokys do you remember why current behavior differs from WPF?

maxkatz6 avatar Aug 07 '22 02:08 maxkatz6

I think I had some issues with DynamicResources in WPF which I don't see in Avalonia. I don't know if that's due to what you described in your issue or not. Nevertheless, I think it's not a big deal to use TryGetResource once you know it?! Is it for you a big deal?

timunie avatar Aug 07 '22 09:08 timunie

It can be confusing. Because indexer gets resource from the dictionary. TryGetResource lookups in the merged dictionaries too. And TryFindResource lookups from the control ancestors' resources.

maxkatz6 avatar Aug 07 '22 09:08 maxkatz6

That's true, but on the other hand it may result in better performance if you know where to look into. Probably the naming should reflect that somehow.

timunie avatar Aug 07 '22 10:08 timunie

I just ran across this one and agree this needs to be changed to follow WPF. Note that UWP and WinUI also search all dictionaries -- even merged ones -- when using the indexer.

What's even worse, there seems to be no one-liner way of getting resources in Avalonia now.

WPF/WinUI allowed: fillColor = (Color)Application.Current.Resources["NeutralValueBackgroundColor"];

Avalonia requires:

Application.Current.Resources.TryGetResource("NeutralValueBackgroundColor", out object resource1);
fillColor   = (Color)resource1;

Which is an extra throw-away variable.

I realize this was done for safety to some degree but the indexer will still throw exceptions if a value isn't found so forcing TryGetResource doesn't even help in all cases.

I would normally expect a generic method here that will return the expected type or default(T). Better to just allow the WPF functionality though.

robloo avatar Oct 30 '22 19:10 robloo

Something like this is a lot more helpful

        /// <summary>
        /// Gets the first resource matching the given key within the application resources dictionary.
        /// </summary>
        /// <typeparam name="T">The type of resource to return.</typeparam>
        /// <param name="app">The application instance.</param>
        /// <param name="key">The resource key.</param>
        /// <returns>The located resource or default(T).</returns>
        public static T GetResource<T>(
            this Application app,
            object key)
        {
            if (Application.Current.Resources.TryGetResource(key, out object resource))
            {
                if (resource is T)
                {
                    return (T)resource;
                }
            }

            return default(T);
        }

robloo avatar Oct 30 '22 20:10 robloo

@maxkatz6 @grokys I propose this issue be resolved for 11.0 as it may be a breaking change. Either:

  1. Close it as "by design" accepting the difference from WPF and the work-around discussed above
  2. Make resource lookup with Application.Current.Resources["ColorConnector"] work in all cases that WPF works (would be best IMO).

robloo avatar Apr 27 '23 11:04 robloo