MAUI Snack - Get ANY Resource Dictionary Value in Code Behind!
Resource Dictionaries enables us to create reusable resources in our XAML files or in code behind! To resolve and access Resource Dictionary Resources
in code behind, we can access it, in some cases, by using the Resource Key
index name in the Resource Dictionary
:
var cornerRadius = Application.Current?.Resources["DefaultCornerRadius"] as CornerRadius;
…or with TryGetValue
…
if (Application.Current?.Resources.TryGetValue("DefaultCornerRadius", out var resource) ?? false)
{
var cornerRadius = resource as CornerRadius;
}
BUT when we need to access a Resource
that is defined with OnPlatform
, OnIdiom
and/or references a DynamicResource
in a Merged Dictionary
, it gets trickier! Especially if this is nested in multiple layers!
Let’s look at an extreme example!
First we have the main Application Resource Dictionary
with the DefaultCornerRadius
resource. Based OnPlatform
the value references a specific DynamicResource
.
<Application.Resources>
<ResourceDictionary>
<OnPlatform x:Key="DefaultCornerRadius" x:TypeArguments="CornerRadius">
<On Platform="Android" Value="{DynamicResource SmallCornerRadius}"/>
<On Platform="iOS" Value="{DynamicResource MediumCornerRadius}/>
</OnPlatform>
</ResourceDictionary>
</Application.Resources>
At runtime, the DefaultStyle
Resource Dictionary is added, based on screen resolution, as a Merged Dictionary
with the values for CornerRadius
.
Resources.MergedDictionaries.Add(new DefaultStyle());
In DefaultStyle
dictionary, both SmallCornerRadius
and MediumCornerRadius
is defined based on Idiom!
<ResourceDictionary
x:Class="RetailOperationApp.Resources.Styles.DefaultStyle"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<OnIdiom
x:Key="SmallCornerRadius"
x:TypeArguments="CornerRadius"
Default="10"
Desktop="11"
Phone="12"
TV="13"
Tablet="14"
Watch="15" />
<OnIdiom
x:Key="MediumCornerRadius"
x:TypeArguments="CornerRadius"
Default="20"
Desktop="21"
Phone="22"
TV="23"
Tablet="24"
Watch="25" />
</ResourceDictionary>
In this case and as far as I know, to resolve the correct CornerRadius for DefaultCornerRadius
requires first resolving the correct OnPlatform
, then finding the DynamicResource
and finally resolve the correct OnIdom
…which makes it tricky to have a easy way of resolving ANY resource!
BUT, what if we could resolve ANY resource like this:
var cornerRadius = ResourcesUtility.FindResource<CornerRadius>("DefaultCornerRadius");
…or…
var cornerRadius = Application.Current?.Resources.FindResource<CornerRadius>("DefaultCornerRadius");
To solve this, I have made the following static class that enables just that!
public static class ResourcesUtility
{
private static T? GetResource<T>(ResourceDictionary resources, object? resource)
{
//Check resource type
switch (resource)
{
case OnPlatform<T> value:
//Get return type platform specific resource
return GetResource<T>(resources, value.Platforms?.FirstOrDefault(p => p.Platform.Contains($"{DeviceInfo.Platform}"))?.Value);
case OnIdiomExtension value:
{
//Get idiom specific resource
var idiomValue = value.Default;
if (DeviceInfo.Idiom == DeviceIdiom.Desktop) idiomValue = value.Desktop;
if (DeviceInfo.Idiom == DeviceIdiom.Phone) idiomValue = value.Phone;
if (DeviceInfo.Idiom == DeviceIdiom.Tablet) idiomValue = value.Tablet;
if (DeviceInfo.Idiom == DeviceIdiom.TV) idiomValue = value.TV;
if (DeviceInfo.Idiom == DeviceIdiom.Watch) idiomValue = value.Watch;
return GetResource<T>(resources, idiomValue);
}
case OnIdiom<T> value:
{
//Get return type idiom specific resource
var idiomValue = value.Default;
if (DeviceInfo.Idiom == DeviceIdiom.Desktop) idiomValue = value.Desktop;
if (DeviceInfo.Idiom == DeviceIdiom.Phone) idiomValue = value.Phone;
if (DeviceInfo.Idiom == DeviceIdiom.Tablet) idiomValue = value.Tablet;
if (DeviceInfo.Idiom == DeviceIdiom.TV) idiomValue = value.TV;
if (DeviceInfo.Idiom == DeviceIdiom.Watch) idiomValue = value.Watch;
return idiomValue;
}
case DynamicResource value:
//Get dynamic resource
return resources.FindResource<T>(value.Key);
case string value:
{
//Attempt to cast to resource type or return default
try
{
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter?.CanConvertFrom(typeof(string)) ?? false)
{
// Cast ConvertFromString(string text) : object to (T)
return (T?)converter.ConvertFromString(value);
}
return default;
}
catch
{
return default;
}
}
case T value:
//Return found resource value
return value;
default:
//Return default resource value
return default;
}
}
public static T? FindResource<T>(this ResourceDictionary resources, string resourceKey)
{
//Verify parameter
if (string.IsNullOrWhiteSpace(resourceKey))
return default;
//Attempt to find resource
if (resources.TryGetValue(resourceKey, out var resource))
//Get resource
return GetResource<T>(resources, resource);
else
//If nothing found, return default
return default;
}
public static T? FindResource<T>(string resourceKey)
{
//Verify Current Application
if (Application.Current is not null)
//Attempt to find resource
return Application.Current.Resources.FindResource<T>(resourceKey);
else
//If nothing found, return default
return default;
}
}
So with this we have a easy way to “Get ANY Resource Dictionary Value in Code Behind”. Enjoy!