Silverlight Resource Markup Extension
In case you want to use resources in your Silverlight applications, you have a couple of options. My favorite, however, is using a markup extension and RESX resource files. With this approach, you can have code such as:
<Button Click="OnClick" Content="{my:Resource ResourceName=Resources, ResourceKey=ButtonTitle}"/>
This is very similar to how you'd do it in ASP.NET web forms, and you can even share the resource files. Let's see the ResourceExtension class, which, by implementing IMarkupExtension<T>, becomes a markup extension, meaning that by convention we can leave out the Extension suffix:
public sealed class ResourceExtension : IMarkupExtension<String>
{
public String ResourceName { get; set; }
public String ResourceKey { get; set; }
private Type FindResourceType(params Assembly [] assemblies)
{
foreach (var assembly in assemblies.Distinct())
{
var resourceType = assembly.GetTypes().SingleOrDefault(x => (x.BaseType == typeof(Object)) && (x.Name == this.ResourceName));
if (resourceType != null)
{
return resourceType;
}
}
return null;
}
public String ProvideValue(IServiceProvider serviceProvider)
{
var executingAssembly = Assembly.GetExecutingAssembly();
var callingAssembly = Assembly.GetCallingAssembly();
var applicationAssembly = Application.Current.GetType().Assembly;
var resourceType = this.FindResourceType(applicationAssembly, callingAssembly, executingAssembly);
if (resourceType != null)
{
var resourceManager = new ResourceManager(resourceType);
return resourceManager.GetString(this.ResourceKey);
}
else
{
throw new InvalidOperationException(String.Format("Cannot find resource {0} with key {1}.", this.ResourceName, this.ResourceKey));
}
}
}
ResourceExtension will try to find the class identified in the ResourceName property in a couple of assemblies and if it finds it, will try to return the resource key from the ResourceKey property; otherwise, it throws an exception, because, obviously, something is wrong.
The resource files need to be compiled as embedded resources:
Now, before you can use this, you need to do a couple of things:
- Add the NeutralResourcesAssemblyAttribute to your assembly to indicate which language the default resource file refers to (in AssemblyInfo.cs):
[assembly: NeutralResourcesLanguage("en-US")]
- Set the culture of the current thread upon application startup (App.xaml.cs):
private void Application_Startup(object sender, StartupEventArgs e)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("pt-PT");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
this.RootVisual = new MainPage();
}
- Add the list of supported languages to the project file (.csproj):
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4AB634F1-D00D-4461-83F4-3ADA1DF2D47B}</ProjectGuid>
<ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<SupportedCultures>en-US;pt-PT</SupportedCultures>
<TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
<SilverlightApplication>true</SilverlightApplication>
<!- ... -->
</PropertyGroup>
</Project>
And this is it! Enjoy!