Wednesday, March 21, 2012

Building Global Windows 8 Metro XAML Apps Part 1–Localizing Strings


WinRT provides good support for building global aware applications especially when compared to classic Windows API , but has some limitations compared to WPF features. In this series of posts we will explore the provided features with some workarounds to the issues in the current release.

ResW Files


WinRT uses ResW files to store localized strings, which are XML files used to store key value . ResW is the same as ResX schema. But unlike ResX built-in tooling in Visual Studio for WPF and Silverlight no strongly typed classes will be generated for ResW files. ResW files are compiled to binary .pri files using  makpri.exe, the same tool can be used to dump the data to XML. Dumping pri files provides useful information about how and what resources are stored.

The resource files can be loaded manually using ResourceLoader or ResourceManager, but the runtime will load the files automatically if the file is named Resources.resw and placed in a folder named with National Language Tag. The following screenshot shows an example of this structure.

project-hierarchy

The folder name is divided into two chunks, the first is the culture identifier for example en to English, es to Spanish, the second is the country qualifier, for example en-US for or en-UK. Determining the right resource to load depends on a heuristic process depending on the user Language Preference list.



Language Preferences


Traditionally .NET applications used the CurrentCutlure to determine the resource to load. This allowed only two options, either the culture specific resource or the fallback resource. With Windows 8 comes the concept of Language preferences which can include multiple language. The resource engine will try loading resources for the preferred language and if it can’t find any it will try the preferred language culture then the second preferred language and so on and if no match from the preferred languages is found, the engine will default to the fallback language. You can get the language preferences of the current user with Windows.System.UserProfile.GlobalizationPreferences.Languages property.

You can programmatically override the preferred language using Windows.Globalization.ApplicationPreferences.PreferredLanguage property. The PreferredLanguage setting is persisted, an will live across applications sessions. To clear it to use the system default language preferences instead, set it to an empty string.

Loading Strings in XAML


The resource engine can bind strings automatically to XAML elements if the following steps are followed:

1- Add x:Uid attribute to your XAML element, for example <TextBlock x:Uid="HelloWorld" /> 2- In the Resources.resw file add a value with its key uses the convention [Uid].[Property] for example HelloWorld.Text or HelloWorld.FontSize to set the Text and FontSize properties of the control with x:Uid HelloWorld.

x:Uid’s is not required to be unique. If two elements have the same Uid, both will receive the property settings. The property type is not limited to strings. Any type with built in converter can be used, for example Color properties can be set to Green or #00FF00 for example. Custom converters are not supported using Uid. If you need custom value converters for localized resource strings, you can use Binding to a class that loads the resources manually.

Attached properties can also be set in resw files but requires the fully qualified namespace for the class defining the property, for example the syntax for setting the Grid.Column property for an element would be MyElement.[using:Windows.UI.Xaml.Controls]Grid.Column

Loading Strings Manually


The ResourceLoader class simplifies the task of retrieving a string in two steps. First you instantiate a ResourceLoader then invoke GetString method. Instantiating ResourceLoader without any parameters loads the resources located in Resources.resw class. Passing a name loads the resource in the specified file as shown in the following example
   1: var loader = new ResourceLoader();
   2: loader.GetString("HelloWorld");
   3: var customLoader = new ResourceLoader("Custom");
   4: var str = customLoader.GetString("MyString");

ResourceManager class located in Windows.ApplicationModel.Resources.Core namespace exposes more powerful API to deal with resources, but requires more intimate knowledge of the internal structure of PRI files and resource maps. This is a native class part of WinRT API and different from System.Resources.ResourceManager. To get a string using ResourceManager first navigate to the subtree containing the resource, invoke GetValue or TryGetValue method. GetValue returns a ResourceCandidate that can be converted to a string or to a file depending on resource type. The following snippet shows using ResourceManager to load a string in action


   1: var manager = Windows.ApplicationModel.Resources.Core.ResourceManager.Current;
   2: var subtree = manager.MainResourceMap.GetSubtree("Custom");
   3: var resource = subtree.GetValue("MyString");
   4: var resourceValue = resource.ToString();

Responding to System Preferences Changes


ResourceManager exposes an event that can be used to detect the system preference changes. The following snippet shows how to use this event and get the current language preferences.


   1: var manager = Windows.ApplicationModel.Resources.Core.ResourceManager.Current;
   2: manager.DefaultContext.QualifierValues.MapChanged +=  (s, m) => {
   3:     string currentLangValue;
   4:     ResourceManager.Current.DefaultContext.QualifierValues.TryGetValue("Language", out currentLangValue);
   5:     var langs = Windows.System.UserProfile.GlobalizationPreferences.Languages;
   6: };

In the next post we will dig deeper into PRI files and their internal structure, and see how to load localized files and custom layouts.

Wednesday, March 7, 2012

Using SeaDragon Ajax to Display DeepZoom Content in Windows8 Apps

Download sample project

MultiScaleImage was introduced in Silverlight 2 and offered great power to manipulate high resolution images on the web. Unfortunately MultiScaleImage control is not yet available to Windows 8 developers as of Consumer Preview. DeepZoom or SeaDragon research project cuts images into smaller pyramid of lower resolution tiles, and smartly brings down only the tile suitable for the current display. So a viewer can be built in virtually any technology. Microsoft provides a JavaScript viewer for DeepZoom content. The JavaScript implementation has limited features and does not support image collections and sparse images, but works very well with single images.

Using SeaDragon Ajax in Windows 8 HTML5 apps is straightforward except for few hacks to work around the security restrictions and environment difference from the browser environment.

First you need to get the source from http://gallery.expression.microsoft.com/SeadragonAjax and note that you can not reference the hosted script because access to external scripts is prohibited while in local context http://msdn.microsoft.com/en-us/library/windows/apps/hh465373.aspx.

I used the dev version of the library to face the first glitch. The seadragon-dev.js uses document.write to emit the script files required by the library. WinRT apps running in local context requires dynamic content to be secure using the window.toStaticHTML or else you will receive “JavaScript runtime error: Unable to add dynamic content. A script attempted to inject dynamic content, or elements previously modified dynamically, that might be unsafe.”.

But using the window.toStaticHTML will remove all script tags in dynamic content, which renders the code useless. Since we trust the source of the content we can use MSApp.execUnsafeLocalFunction to wrap the dynamic content creation and temporarily disable the security restrictions as shown in the following snippet.

   1:  MSApp.execUnsafeLocalFunction(function () {
   2:      document.write(html.join(''));
   3:  });




The next glitch was the unavailability of the alert function for WinJS apps, and the library use it to display errors. As a workaround I defined a global alert function that displays a MessageDialog and logs the message to JavaScript console as shown in the following snippet.


   1:  <script type="text/javascript">
   2:      var alert = function(message) {
   3:          console.log(message);
   4:          var dialog = new Windows.UI.Popups.MessageDialog(message);
   5:          dialog.showAsync();
   6:      }
   7:  </script>



The next glitch was an error “JavaScript runtime error: Automation server can't create object”. This error is caused by the library checking for window.ActiveXObject and if defined it tries to use it to create XMLHTTP object. The workaround I used is setting window.ActiveXObject to null prior to creating the viewer. After that the viewer worked flawlessly. Just make sure to include the img directory available with the source to show the icons correctly.

Saturday, March 3, 2012

Consuming SOAP Web Services in Windows 8 HTML5 Applications

Windows 8 brings a great opportunity to use HTML and JavaScript to build rich desktop applications. I am coming from the managed world and used to some productivity features. One of these features is the Add Service Reference dialog and proxy generators for Web Services.

Interacting with SOAP web services involves a lot of overhead in creating envelop and formatting the message. This complexity is usually hidden behind proxies generated through the rich WSDL meta-information.

Unfortunately JavaScript has poor tooling support for interacting with SOAP services implemented with WCF or any other technology. The best approach when interacting with backend in JavaScript is to use REST based services that follows the simple HTTP protocol rules and returns JSON or XML formatted messages. But this option is not always available to developers.

Fortunately for Windows 8 developers the ability to implement WinRT components in managed languages and expose them to WinJS based applications. In this post the technique to expose a proxy for SOAP web services to HTML5 applications.

First add a new class library project to your HTML5 solution
add_project

Make sure that the output of this class library is WinMD file
winmd

Now use the Add Service Reference dialog to generate the proxy for the Web Service. The important trick here is to make sure that the proxy class are marked as Internal. WinRT exposed classes must follow some rules including being Sealed classes and expose WinRT compatible types. The proxy generated by the Add Service Reference dialog does not follow these rules, therefore the proxy should be internal or you will receive compiler error.
service-reference

You will notice that all the generated methods are Async operations. This is to follow Windows 8 rules of responsive UI. The new C# and VB language features facilitate the usage of async operation. Async methods requires the return type to be either void or System.Task<T>. But System.Task is not a supported WinRT type and if you exposed an async method that returns data as a public method you will get a compiler error

“Error    3    Method has a parameter of type 'System.Threading.Tasks.Task' in its signature.  Instead of using a managed Task type, consider using Windows.Foundation.IAsyncAction, Windows.Foundation.IAsyncOperation, or one of the other Windows Runtime async interfaces.  The standard .NET awaiter pattern also applies when consuming Windows Runtime async interfaces.  Please see System.Runtime.InteropServices.WindowsRuntime.AsyncInfoFactory for more information on converting managed task objects to Windows Runtime async interfaces.   “

To work around this limitation wrap your async operation in a private or internal method and declare a public method that returns Windows.Foundation.IAsyncOperation<T> that wraps the async operation. The following code shows this technique in consuming the public Web Service for 2010 Soccer World Cup located at http://footballpool.dataaccess.eu/data/info.wso?WSDL

   1:  public static Windows.Foundation.IAsyncOperation<TeamsData> GetData()
   2:  {
   3:      return getTeams().AsAsyncOperation();
   4:  }
   5:   
   6:  internal static async Task<TeamsData> getTeams()
   7:  {
   8:      return await Task.Run(async () =>
   9:      {
  10:          var client = new InfoSoapTypeClient();
  11:          var result = await client.TeamsAsync();
  12:   
  13:          var items = from i in result.Body.TeamsResult select i.sName;
  14:          return new TeamsData { Names = items.ToArray() };
  15:      });
  16:  }



Now in your HTML5 application you can add a reference to the managed project and use JavaScript futures to invoke the async operation as shown in the following code


   1:  var proxy = Backend.ServiceProxy;
   2:  proxy.getData().then(function (result) {
   3:      //do something
   4:  });



You can download the sample code here