Reputation: 155
To save having to write the syntax <converters:MyConverter x:Key="myConverter"/>
in a Resources section of XAML in every control I want to use a converter as a static resource, I had the idea to try add every converter in the scope of that control to its resource dictionary programmatically.
What I have done is to create my own custom ControlBase
class which derives from UserControl
which I would then inherit any of my custom controls from rather than UserControl
. ControlBase
is responsible for loading any resources (converters, images, template selectors, etc.) in its own assembly and a MediaLibrary into the Resources
property of itself. It also has a protected method called LoadCallerResources(ResourceTypes resourceTypes)
which loads any resources of the specified types from the derived control into the Resources
dictionary.
The method which is core to all this functionality is as follows. Note that I am using several of my own extension methods and supplementary methods in this code such as "AsEnumerable" on the ResourceTypes flags enum, "ThrowArgumentNullExceptionIfNull", "ToCamelCase" and "GetResourceTypePlurals" used to get the plural name of each resource type on the provided enum (which corresponds to the directory the resources of that type are expected to be in).
/// <summary>
/// Loads all .g.resources files found within the provided assembly that meet the requirements for the specified resource type into the <see cref="FrameworkElement.Resources"/> collection.
/// </summary>
/// <param name="resourceTypes">The type of data in the .g.resources files to load.</param>
/// <param name="resourceAssembly">The assembly to load the resources from.</param>
/// <exception cref="ArgumentNullException">Thrown if resourceAssembly is null.</exception>
private void LoadAssemblyResources(ResourceTypes resourceTypes, Assembly resourceAssembly)
{
Stream manifestResourceStream = resourceAssembly
.ThrowArgumentNullExceptionIfNull(nameof(resourceAssembly))
.GetManifestResourceStream(resourceAssembly.GetName().Name + ".g.resources");
if (manifestResourceStream != null)
using (ResourceReader resourceReader = new ResourceReader(manifestResourceStream))
foreach (DictionaryEntry relevantDictionaryEntry in resourceReader
.Cast<DictionaryEntry>()
.Where(dictionaryEntry =>
{
string resourceFilePath = dictionaryEntry.Key.ToString();
string resourceDirectory = Path.GetDirectoryName(dictionaryEntry.Key.ToString());
string resourceFileName = Path.GetFileName(resourceFilePath);
return string.IsNullOrEmpty(resourceDirectory) && string.Equals(Path.GetExtension(resourceFileName), ".baml", StringComparison.OrdinalIgnoreCase) && resourceTypes.AsEnumerable().Any(resourceType => resourceFileName.StartsWith(Enum.GetName(typeof(ResourceTypes), resourceType), StringComparison.OrdinalIgnoreCase))
|| GetResourceTypesPlurals(resourceTypes).Any(resourceTypePlural => resourceDirectory.Contains(resourceTypePlural, CompareInfo.GetCompareInfo(CultureInfo.InvariantCulture.Name), false));
}))
Resources.Add(Path.GetFileNameWithoutExtension(relevantDictionaryEntry.Key.ToString()).ToCamelCase(), relevantDictionaryEntry.Value);
}
There are a few issues I am having with this code:
StaticResource
bindings to the converters, even when spelt in all -lowercase lettering, give the compiler error of 'The resource "myconvertername" could not be resolved.'My ultimate goal is to be able to reference static resources in XAML without having to explicitly declare them in the dictionary of the control. How would I do this?
Upvotes: 1
Views: 2675
Reputation: 5234
To save having to write the syntax in a Resources section of XAML
The approach i favor for this situation is to have my converters implement MarkupExtension. Doing so makes it so that you don't need to declare it as a resource in Xaml.
public class InvertedBooleanConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return null; //put logic here
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null; //put logic here
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
You can then use it as such in xaml:
IsEnabled="{Binding someValue, Converter={yourNamespace:InvertedBooleanConverter}}"
Upvotes: 3