Adventures on the edge

Learning new development technologies
    "When living on the bleeding edge - you sometimes have to bleed" -BillKrat

Universal Windows: ResourceMap Not Found / How to programmatically retrieve resources with properties

This adventure cost me a few hours of my life…

This occurred when I tried to programmatically get a string from the resource loader.

image

If I called the resource file Resources.resw and placed it in my Strings\en-us path of my application then it sorta-kinda works. 

It sort-kinda works because where you can get simple content, e.g., “TestString” as shown on line 49 in the line below (infoR variable in red box of bottom pane), you can’t programmatically retrieve the control resources such as EmailTextBlock.Text and LostPasswordButton.Content.  Localization did however work using the XAML UID – just not programmatically.

I have a work-around for that as well but first let’s address the error that started all of this.

image

 

The problem was that I had a Gwn.Library.Desktop project that has its own resource file and I needed to reference the resources from a view within this very same library.  Long story short, I could not get localization to work at all (programmatically or through binding) unless the Application had a copy of the resource file.  

To resolve this issue I opted to have a “linked” copy of the resource file from my Gwn.Library.Desktop project in the Application – this way I only have to update one file.  

  1. I named my resource file DesktopResources.resw
  2. Right click on this file and copied the path to clipboard
  3. Navigated to my Application Strings\en-use path and then right clicked on it
  4. Added existing item and pasted the path into the File name box.
  5. Selected the dropdown (right down triangle) and selected “Add as Link”

With this change done I now had localization that worked with XAML UID (as expected) as well as the ability to programmatically retrieve strings using a my GetLocalizedText<T>() method.  Note that in this method (line 34 of image below) that I have to specify the name of my resource file in the ResourceLoader constructor.

image

Likewise I had to ensure that my UID settings were prefixed with the resource filename (not required if you use the default name of resources.resw). 

image


OCT-25-2016 Thank you Matt Cecile for providing the following great tip:
   You say " I cannot retrieve resources that have a property attached". Its simple actually:
          var resourceLoader = new ResourceLoader("DesktopResources");
          var value = resourceLoader.GetString(resourceName + "/Text");
    or /Content, or whatever property you are trying to get.

With the environment setup now, and localization working within XAML, I now have to work-around the issue presented above where I cannot retrieve resources that have a property attached.  To do this I programmatically build a XAML control using the provided type, load it, and then cast the resulting instance as required to get the Text or Content value using the extension method below:

/// <summary>
/// Class StringExtension.
/// </summary>
public static class StringExtension
{
/// <summary>
/// Gets the localized text.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resourceName">Name of the resource.</param>
/// <returns>System.String.</returns>
public static string GetLocalizedText<T>(this string resourceName)
{
var resourceLoader = new ResourceLoader("DesktopResources");
var value = resourceLoader.GetString(resourceName);

if (string.IsNullOrEmpty(value))
{
var ctrlType = typeof(T).Name;

var xaml =
$"<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
$"<{ctrlType} xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' " +
$"x:Uid='{resourceName}' x:Name='Content' />" +
$"</Grid>";

var control = XamlReader.Load(xaml) as Grid;
var result = control?.FindName("Content");
if (result == null) return "";

switch (ctrlType)
{
case "TextBlock": return ((TextBlock)result).Text;
case "Button": return ((Button)result).Content?.ToString();
default:
return "";
}
}
return value;
}
}


 
Below, underlined in yellow, I show how I use this extension method to get the button content so that I can programmatically update my Register and Lost Password buttons – required because I reused this buttons on a registration view and changed their names while in that view.

image
 
Comments are closed