3

Can someone show me the Linq query that would be used to select the AdjustmentsIcon style in the IconResources.xaml file below?

I know you can get to ...

Application.Current.Resources["key"]

but I am looking for a code efficient way to select out the style from the MergeDictionary using Linq.

App.xaml

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:font="clr-namespace:WP.Device.Resources"
             xmlns:resources="clr-namespace:WP.Device.Resources"
             x:Class="WP.Device.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <resources:IconResources />
                <resources:ColorResources />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

IconResources.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:font="clr-namespace:WP.Device.Resources"
                    xmlns:resources="clr-namespace:WP.Device.Resources"
                    x:Class="WP.Device.Resources.IconResources">
    <ResourceDictionary.MergedDictionaries>
        <resources:ColorResources />
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="Label" x:Key="AddNewIcon">
        <Setter Property="FontSize" Value="30" />
    </Style>
    <Style TargetType="Label" x:Key="AdjustmentsIcon">
        <Setter Property="FontSize" Value="40" />
    </Style>
</ResourceDictionary>

UPDATE

I appreciate @pinedax's answer, but for me...

Application.Current.Resources["key"]

doesn't have the keys from the merged dictionaries. I was not able to formulate a Linq query to find my style, but I wrote the following that works...

public Style FindStyle(ResourceDictionary resourceDictionary, string key)
{
    Style style = resourceDictionary.ContainsKey(key) ? resourceDictionary[key] as Style : null;

    if (style == null)
    {
        foreach (var mergedDictionary in resourceDictionary.MergedDictionaries)
        {
            style = FindStyle(mergedDictionary, key);

            if (style != null) break;
        }
    }
    
    return style;
}

and is called with...

Style errorIcon = FindStyle(Application.Current.Resources, "AddNewIcon");
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
John Livermore
  • 30,235
  • 44
  • 126
  • 216

2 Answers2

5
Application.Current.Resources.TryGetValue("key", out var result);

will also look in the merged dictionaries (as stated by @pinedax here).

spottedmahn
  • 14,823
  • 13
  • 108
  • 178
  • I just ran into this issue and am kinda confused. I thought that: Application.Current.Resources["key"] would have also checked the MergedDictionariies right? I am finding this is not the case however. – Reza Jun 15 '21 at 19:00
3

I do encourage you to use the default way of fetching resources out of a ResourceDictionary as for me it is very efficient.

If you look at the ResourceDictionary source code (here) you will see there's an internal dictionary that holds all the resources keys, so when we do:

Application.Current.Resources["key"]

This is actually getting the value from that internal dictionary and dictionary lookup by key is very efficient.

But to answer your question, you should be able to get the values using Linq by using the SelectMany method on the MergeDictionaries property. Something like this:

var mergedDictionary = Application.Current.Resources.MergedDictionaries;
var resourceK = mergedDictionary.SelectMany(x => x)
                                .Where(v => v.Key == "Key")
                                .Select(t => t.Value)
                                .FirstOrDefault();

Hope this helps.-

pinedax
  • 9,246
  • 2
  • 23
  • 30
  • Thank you. I actually tried Application.Current.Resources["key"] originally (and would prefer that), but it couldn't find my key. I assumed it was because the resource was in a merged dictionary and not the main dictionary. – John Livermore Oct 08 '19 at 10:16
  • Hello - are you sure `Resources["key"]` works? I think you have to use `TryGetValue("key", out var result)`. – spottedmahn Aug 10 '20 at 18:17
  • @spottedmahn `Resources` is `DictionaryResource` which is a class that implements `IDictionary`. This says that `Resouces` is a C# Dictionary and as a Dictionary it's possible getting a value indexing with the key as dictionary["key"]. `TryGetValue()` will do the same in a safer way since this one will not raise an exception if the key is not present while the former will. – pinedax Aug 13 '20 at 04:44
  • 1
    Hi pinedax - even if the key is present it wasn’t working. Using the indexer wasn’t searching the MergedDictionaries. I’ll whip a sample app and share it. Thanks for the reply. – spottedmahn Aug 13 '20 at 04:48
  • 1
    Right I got your point! Looking at the Xamarin.Forms code I see the reason why (https://github.com/xamarin/Xamarin.Forms/blob/719fc7a604ff0cce8922d717c99bfb0fa17e35e0/Xamarin.Forms.Core/ResourceDictionary.cs#L268) . `TryGetValue` will do the lookup also in all the MergedDictionaries of the Resources (as you stated). Your answer is correct. My doubt here is why the behaviour is different. Thanks for your clarification @spottedmahn . – pinedax Aug 13 '20 at 05:00
  • Feel free to update your answer and I’ll delete mine as I only figured it b/c of your link to the code!! – spottedmahn Aug 13 '20 at 05:10
  • I'm thinking if they need to [override the indexer](https://stackoverflow.com/questions/287928/how-do-i-overload-the-square-bracket-operator-in-c) to search the `MergedDictionary`'s. – spottedmahn Aug 13 '20 at 13:04
  • 1
    @spottedmahn for me is OK. I will update my Answer but keep yours. Re Indexer: Yeah I believe is the best so that we (developers) will be getting the same result regardless of the method used. – pinedax Aug 13 '20 at 13:13
  • 1
    Looking closely at the code it seems they already did that work of overriding the indexer of the `ResourceDictionary` class: (https://github.com/xamarin/Xamarin.Forms/blob/b3b9c96b0db50e769a92ad17678f534c9093580d/Xamarin.Forms.Core/ResourceDictionary.cs#L196) not sure when this was done so not sure if this is already part of the latest Xamarin release. – pinedax Aug 13 '20 at 13:20