Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MarkupExtension for Resources and Settings

0.00/5 (No votes)
25 May 2016 1  
Presents simple MarkupExtension for accessing the Properties.Resources and Properties.Settings files. Also, with Resources, which are always strings, conversion is supported.

Introduction

I was unable to user Binding to connect to a Properties.Resources value with an x:Static Source. Apparently using the x:Static does work for other people, It is interesting because in C# this appears to be a singleton, but using XAML does not seem to work. It seemed like maybe this is a place to use a MarkupExtension. Apparently using the x:Static does work for other people, but despite requests for understanding why, I have been unable to determine how to make the x:Static work for Resources.

Since the original submittal of this article, I have significantly enhanced the ResourcesExtension class.

Implementation for Accessing Properties.Resources

The basic design of the MarkupExtension for accessing a Properties.Resources value is very straightforward. It has a few properties for the resource Key, Converter and ConverterParameter, and the ProvideValue method: 

 public class ResourcesExtension : MarkupExtension
 {
  readonly WeakReference _object;

  public ResourcesExtension(string key) { Key = key; }

  [ConstructorArgument("key")]
  public string Key { get; set; }

  public IValueConverter Converter { get; set; }

  public object ConverterParameter { get; set; }

  public override object ProvideValue(IServiceProvider serviceProvider)
  {
   var service = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
   if (service == null) return null;
   Debug.Assert(!string.IsNullOrEmpty(Key), "Resource name not provided");
   var value = Properties.Resources.ResourceManager.GetObject(Key);
   Debug.Assert(!string.IsNullOrEmpty(Key), $"Resource with name '{Key}' not found");

   if (Converter != null)
   {
    value = Converter.Convert(value, ((DependencyProperty)service.TargetProperty).PropertyType,
      ConverterParameter, GetCulture()
      );
    return value;
   }
   else
   {
    var type = ((DependencyProperty)service.TargetProperty).PropertyType;
    TypeConverter converter = TypeDescriptor.GetConverter(type);
    Debug.Assert(converter != null, $"Converter for Type of '{type}' not found");
    return converter.ConvertFrom(value);
   }
  }

   return CultureInfo.DefaultThreadCurrentUICulture;
 }

There is, however, one serious problem, and that is that a resource are a string value. That is fine for a lot of applications, but if it is to be bound to a bool property such as IsEnabled, or a Visibility property, this will not work. There is information in the IServiceProvider about target type, but only as a private value, and so, unfortunately, the serviceProvider argument is not particularly useful in figuring out what Type the value should be.

One of the most important things I learned about that really made this class capaable was GetService method. The GetService method of the IServiceProvider argument exposes all the information required to support use use of IValueConverter and IMultiValueConverter, which means it also provides the target Type.

First the value for the resource is aquired using the Properties.Resources.ResourceManager.GetObject method. If a Converter is speicifed, then the Converter is used to return the value. Otherwise the object returned from the GetService method is used to ther the PropertyType, which is used to get the TypeConverter, which is used to convert the string value (a resource must be a string) to the proper target Type.

Using MarkupExtension to Access Resources

Using this for a simple case where the string value can be used directly is really easy:

<TextBlock HorizontalAlignment="Center"
           VerticalAlignment="Center" IsEnabled="False"
           Text="{local:Resources FlagTrue}" />

All that is needed is to define a namespace prefix to tell the XAML where to find the MarkupExtension.

If need to convert the value for a property that does not accept a string, it becomes a little more complex:

<CheckBox Grid.Row="3" HorizontalAlignment="Center"
           VerticalAlignment="Center" IsChecked="{local:Resources FlagFalse, Type=sys:Boolean}"
           Content="Resources False Boolean Test" />

Need to add the Type definition, and have to have another namespace prefix to tell the XAML where to find the Type, in this case the type bool is in the System namespace, so would have to include the following namespace prefix defintion:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

Using the x:Static for Resources

It has been suggested that Resources can be accessed directly using the x:Static:

<TextBox Grid.Row="5"
         Grid.Column="1"
         HorizontalAlignment="Center"
         VerticalAlignment="Center"
         IsReadOnly="True"
         Text="{x:Static resx:Resources.FlagFalse}" />

The namespace (xmlns) for this binding is:

xmlns:resx="clr-namespace:ResourceMarkupExtensionSample.Properties"

This does not work for me:

ResourceMarkupExtensionSample.Properties.Resources.FlagFalse' StaticExtension value cannot be 
resolved to an enumeration, static field, or static property.

 Another problem is that automatic conversion to non-string values does not work.

Implementation for Accessing Properties.Settings

This same concept can be used even better for accessing the Setting file:

public class SettingsExtension : MarkupExtension
{
 private string _key;

 public SettingsExtension(string key)
 {
  _key = key;
 }

 [ConstructorArgument("key")]
 public string Key
 {
  get { return _key; }
  set { _key = value; }
 }

 public override object ProvideValue(IServiceProvider serviceProvider)
 {
  Debug.Assert(!string.IsNullOrEmpty(Key), "Settings name not provided");
  var value = Properties.Settings.Default.Properties[Key];
  Debug.Assert(!string.IsNullOrEmpty(Key), $"Settings with name '{Key}' not found");
  TypeConverter converter = TypeDescriptor.GetConverter(value.PropertyType);
  Debug.Assert(converter != null, $"Converter for Type of '{value.PropertyType}' not found");
  return converter.ConvertFrom(value.DefaultValue);
 }
}

Each settings actually has information on the Type, so can automatically convert to the desired Type. There is commented code in the solution will prove this. Interestingly, the problem is only discovered when the applicaiton is run, under debug, it seems to work.

Because you can specify type, I did not feel the need to add the complexity that I did for Resources where type canno

Using MarkupExtension to Access Settitngs

Basically the same method is used to use this class as used for the PropertiesExtension:

<CheckBox Grid.Row="4" HorizontalAlignment="Center"
           VerticalAlignment="Center" IsChecked="{local:Settings SettingsTrue}"
           Content="Settings True Boolean Test" />

The Sample

This sample shows Binding a TextBox and a CheckBox to a Resource (which is always a string) with the value true and false, and a Binding Setting to the bool true value. There is also commented out code that will for Binding a TextBox to a Resource using x:Static, and a CheckBox. As stated above, I commented out the TextBox because of a run time error. If that works for you, you can see the issue with binding a Resource to the IsChecked property of a CheckBox also.

If you can Help Make this Better

When I created the code to handle the converter, I use the CultureInfo.DefaultThreadCurrentUICulture to get the culture for the IValueConverter. I am not sure this is the best way..

Conclusion

Although I would have liked being able to use the x:Static to get values from the Resources file, I actually like the simplicity of this better. Also, with the use of GetService and casting the result to IProvideValueTarget, a TypeConverter can be used to covert to the string value to the right Type. For Resources, which are strictly string values, this is a significant advantage. 

I was not able to use the x:Static, but apparently other people can. If x:Static does not work on your project, at least there is an option.

History

  • 05/25/2016: Initial version.
  • 07/06/2016: Updated version that eliminates need to specify type for binding to resources.
  • 07/15/2016: Updated version adds converter to binding to settings.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here