The missing ResWFileCodeGenerator custom tool for Visual Studio 2012

I’ve been doing quite a lot of Windows Store Apps on Windows 8 lately. Ever since I started I always seemed to miss the ResXFileCodeGenerator custom tool. This is because in WinRT when you want to load a localized string in code then you will have to do something like this:

var resources = new ResourceLoader();
var localizedString = resources.GetString("SomeResourceName");

This of course can easily lead to a lot of errors. In my case I got a little to eager while refactoring and forgot to notice that I was also changing hard coded strings. This broke the code quite a lot. Because of this frustration I decided to implement my own custom tool since Microsoft didn’t provide one

The project is open source and is available at Github. Here’s a preview of the description which I took directly from my CodePlex project site.

Download from Visual Studio Marketplace

Features

  • Define custom namespace for the generated file
  • Auto-updating of generated code file when changes are made to the .ResW Resource file
  • XML documentation style comments like “Localized resource similar to ‘[the value]’”
  • Supports Visual Studio 2015, 2017, 2019, and 2022
  • Supports dotted keys - Replaces . with _ (e.g. Something.Awesome = Something_Awesome)

Custom Tools

  • ReswFileCodeGenerator - Generates a public class
  • InternalReswFileCodeGenerator - Generates an internal (C#) / friend (VB) class

Supported Languages

  • C#
  • Visual Basic

Screenshots

ReswFileCodeGenerator Custom Tool

InternalReswFileCodeGenerator Custom Tool

C# Usage

string test1, test2, test3;

void LoadLocalizedStrings()
{
    test1 = App1.LocalizedResources.Resources.Test1;
    test2 = App1.LocalizedResources.Resources.Test2;
    test3 = App1.LocalizedResources.Resources.Test3;
    test4 = App1.LocalizedResources.Resources.Test_With_Dotted_Keys;
}

Visual Basic Usage

Dim test1, test2, test3

Private Sub LoadLocalizedStrings()
    test1 = AppVb.LocalizedStrings.Resources.Test1
    test2 = AppVb.LocalizedStrings.Resources.Test2
    test3 = AppVb.LocalizedStrings.Resources.Test3
    test4 = AppVb.LocalizedStrings.Resources.Test_With_Dotted_Keys;
End Sub

Generated C# Code

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------------------
// <auto-generatedInfo>
// 	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
// 	ResW File Code Generator was written by Christian Resma Helle
// 	and is under GNU General Public License version 2 (GPLv2)
// 
// 	This code contains a helper class exposing property representations
// 	of the string resources defined in the specified .ResW file
// 
// 	Generated: 05/20/2019 15:47:37
// </auto-generatedInfo>
// --------------------------------------------------------------------------------------------------
namespace App2
{
    using Windows.ApplicationModel.Resources;


    public sealed partial class Resources
    {
    
        private static ResourceLoader resourceLoader;
    
        /// <summary>
        /// Get or set ResourceLoader implementation
        /// </summary>
        public static ResourceLoader Resource
        {
            get
            {
                if ((resourceLoader == null))
                {
                    Resources.Initialize();
                }
                return resourceLoader;
            }
            set
            {
                resourceLoader = value;
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test
        {
            get
            {
                return Resource.GetString("Test");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test2
        {
            get
            {
                return Resource.GetString("Test2");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test3
        {
            get
            {
                return Resource.GetString("Test3");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test_With_Dotted_Keys
        {
            get
            {
                return Resource.GetString("Test/With/Dotted/Keys");
            }
        }
    
        public static void Initialize()
        {
            string executingAssemblyName;
            executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName;
            string[] executingAssemblySplit;
            executingAssemblySplit = executingAssemblyName.Split(',');
            executingAssemblyName = executingAssemblySplit[1];
            string currentAssemblyName;
            currentAssemblyName = typeof(Resources).AssemblyQualifiedName;
            string[] currentAssemblySplit;
            currentAssemblySplit = currentAssemblyName.Split(',');
            currentAssemblyName = currentAssemblySplit[1];
            if (executingAssemblyName.Equals(currentAssemblyName))
            {
                resourceLoader = ResourceLoader.GetForCurrentView("Resources");
            }
            else
            {
                resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources");
            }
        }
    }
}

Generated Visual Basic Code

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:4.0.30319.42000
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports Windows.ApplicationModel.Resources

'--------------------------------------------------------------------------------------------------
'<auto-generatedInfo>
'	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
'	ResW File Code Generator was written by Christian Resma Helle
'	and is under GNU General Public License version 2 (GPLv2)
'
'	This code contains a helper class exposing property representations
'	of the string resources defined in the specified .ResW file
'
'	Generated: 05/20/2019 15:48:18
'</auto-generatedInfo>
'--------------------------------------------------------------------------------------------------

Partial Public NotInheritable Class Resources

    Private Shared resourceLoader As ResourceLoader

    '''<summary>
    '''Get or set ResourceLoader implementation
    '''</summary>
    Public Shared Property Resource() As ResourceLoader
        Get
            If (resourceLoader Is Nothing) Then
                Resources.Initialize
            End If
            Return resourceLoader
        End Get
        Set
            resourceLoader = value
        End Set
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test() As String
        Get
            Return Resource.GetString("Test")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test2() As String
        Get
            Return Resource.GetString("Test2")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test3() As String
        Get
            Return Resource.GetString("Test3")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test_With_Dotted_Keys() As String
        Get
            Return Resource.GetString("Test/With/Dotted/Keys")
        End Get
    End Property

    Public Shared Sub Initialize()
        Dim executingAssemblyName As String
        executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName
        Dim executingAssemblySplit() As String
        executingAssemblySplit = executingAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        executingAssemblyName = executingAssemblySplit(1)
        Dim currentAssemblyName As String
        currentAssemblyName = GetType(Resources).AssemblyQualifiedName
        Dim currentAssemblySplit() As String
        currentAssemblySplit = currentAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        currentAssemblyName = currentAssemblySplit(1)
        If executingAssemblyName.Equals(currentAssemblyName) Then
            resourceLoader = ResourceLoader.GetForCurrentView("Resources")
        Else
            resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources")
        End If
    End Sub
End Class



Parenthood

Once again I have taken a break from blogging. Parenting and my new family has been my absolute top priority and I’ve been having a hard time finding the time to write articles. I really enjoy spending time with my son and wife and I take advantage of every possible moment to do so.

I ended up at a schedule that starts around 5:30 AM in which my son wakes up, and wakes me up. I go and start preparing some breakfast for him. We then have a cozy morning playing around the house. I drop him off at day care as late possible (in my case 9:00 AM) and then drive to the office, or to a customer, or back home if I’m working from home. I pick my son up at between 3:00-4:00 PM and spend time with him and my wife until around 6:30 PM (this is his bed time and I’m very lucky that he has no trouble sleeping). From around 6:30 to 8:00-9:00 PM I spend some time with my wife discussing how our days went and do some planning for the following days. My wife goes to bed a bit early, and when that happens I go back to work. Since I drastically shortened by day, I need to do quite a bit of catching up at night. I usually work an extra 2-3 hours more at night, then I have an hour or 2 to give my mind a rest or work on a hobby project. My day usually ends between 12:30 AM and 2:00 AM. I need at least 4 hours of sleep to recharge for the next day, otherwise I won’t be able to last a full week on my schedule.

Hence the break from blogging :)



Windows Phone and HTML5 - Danish Developer Conference 2012 Session Recording

Here’s the recording of my session on Windows Phone and HTML5 for the Danish Developer Conference 2012, for those who missed it…

You can also watch it on YouTube



Accessing the Accelerometer from HTML5 and Javascript with Windows Phone 7

In my previous post I discussed how to have Javascript code hosted in a WebBrowser control execute .NET code in the host application and vice versa. I also demonstrated how to retrieve and display device status information using HTML5 hosted in a WebBrowser control.

For this sample I would like to demonstrate how to access the Accelerometer sensor from HTML5 and Javascript. To make things more interesting, the Accelerometer reading data will be constantly updated every 100 milliseconds and .NET code will repeatedly call a Javascript method as Accelerometer reading data gets updated

[

[

And here’s the code…

Default.html (HTML5 + Javascript)

The code below is going to be used as a local html file that is to be copied to isolated storage. Let’s put this in a folder called HTML

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=480, height=800, user-scalable=no" />
    <meta name="MobileOptimized" content="width" />
    <meta name="HandheldFriendly" content="true" />
    <title>HTML5 and Windows Phone 7</title>
    <style>
        body
        {
            color: White;
            background-color: Black;
            font-family: 'Segoe WP Semibold';
            text-align: left;
        }
        h3
        {
            font-size: 20pt;
        }
        input
        {
            color: #ffffff;
            background-color: #000000;
            border: 2px solid white;
            vertical-align: baseline;
            font-size: 17pt;
            min-width: 40px;
            min-height: 40px;
            margin: 5;
        }
    </style>
</head>
<body onload="onLoad()">
    <div>
        <h3>X:</h3>
        <input id="x" type="text" value="0" />
        <h3>Y:</h3>
        <input id="y" type="text" value="0" />
        <h3>Z:</h3>
        <input id="z" type="text" value="0" />
    </div>

    <script type="text/javascript">
        function onLoad() {
            window.external.notify("startAccelerometer");
        } 

        function accelerometerCallback(x, y, z) {
            document.getElementById("x").value = x;
            document.getElementById("y").value = y;
            document.getElementById("z").value = z;
        }
    </script>
</body>
</html>

MainPage.xaml

The code below is the main page of the Silverlight application that will host the HTML content

<phone:PhoneApplicationPage x:Class="PhoneApp.MainPage"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                           xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           mc:Ignorable="d"
                           d:DesignWidth="480"
                           d:DesignHeight="768"
                           FontFamily="{StaticResource PhoneFontFamilyNormal}"
                           FontSize="{StaticResource PhoneFontSizeNormal}"
                           Foreground="{StaticResource PhoneForegroundBrush}"
                           SupportedOrientations="Portrait"
                           Orientation="Portrait"
                           shell:SystemTray.IsVisible="True"
                           Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot"
         Background="Transparent">
        <phone:WebBrowser Name="browser"
                         IsScriptEnabled="True"
                         Source="HTML/Default.html"
                         ScriptNotify="browser_ScriptNotify" />
    </Grid>

</phone:PhoneApplicationPage>

MainPage.xaml.cs

And here’s the code behind the xaml file

public partial class MainPage : PhoneApplicationPage
{
    private Microsoft.Devices.Sensors.Accelerometer accelerometer; 

    public MainPage()
    {
        InitializeComponent();
    } 

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (!store.DirectoryExists("HTML")) store.CreateDirectory("HTML");
            CopyToIsolatedStorage("HTML\\Default.html", store);
        }
    } 

    private static void CopyToIsolatedStorage(string file, IsolatedStorageFile store, bool overwrite = true)
    {
        if (store.FileExists(file) && !overwrite)
            return; 

        using (Stream resourceStream = Application.GetResourceStream(new Uri(file, UriKind.Relative)).Stream)
        using (IsolatedStorageFileStream fileStream = store.OpenFile(file, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            int bytesRead;
            var buffer = new byte[resourceStream.Length];
            while ((bytesRead = resourceStream.Read(buffer, 0, buffer.Length)) > 0)
                fileStream.Write(buffer, 0, bytesRead);
        }
    } 

    private void browser_ScriptNotify(object sender, NotifyEventArgs e)
    {
        if (e.Value == "startAccelerometer")
        {
            if (accelerometer == null)
            {
                accelerometer = new Microsoft.Devices.Sensors.Accelerometer { TimeBetweenUpdates = TimeSpan.FromMilliseconds(100) };
                accelerometer.CurrentValueChanged += (o, args) => Dispatcher.BeginInvoke(() =>
                {
                    var x = args.SensorReading.Acceleration.X.ToString("0.000");
                    var y = args.SensorReading.Acceleration.Y.ToString("0.000");
                    var z = args.SensorReading.Acceleration.Z.ToString("0.000"); 

                    browser.InvokeScript("eval", string.Format("accelerometerCallback({0},{1},{2})", x, y, z));
                });

                accelerometer.Start();
            }
        }
    }
}

What happens in the code above is that a Javascript method is executed that notifies the host application telling it to start the Accelerometer when the HTML has loaded. We then add an event handler to the Accelerometers CurrentValueChanged event that invokes the accelerometerCallback Javascript method and passing in Accelerometer reading data as the arguments. Notice that I use eval as the Javascript method to invoke and passing the method call as an argument, this is because the accelerometer reading data is retrieved on a worker thread and for some reason an unknown system error occurs even when executing code on the UI thread through the Page Dispatcher.BeginInvoke() method. I figured out that using eval was the only way to execute Javascript code from a .NET worker thread.

I hope you found this useful. You can grab the full source code for the example here:



Integrating HTML5 and Javascript with Windows Phone 7

Everyone, everywhere is talking about HTML5 these days. I myself did a recent talk at the Danish Developer Conference 2012 on Windows Phone and HTML5. I get the point and I see the power and beauty of HTML5. But, HTML5 is as far as I can see not entirely ready yet and as for mobile applications, I would always choose writing a native application that takes full advantage of the platform and not just an application that runs in a browser, even if the the browser component is hosted in a native application. I think developers should really learn to appreciate the platform more.

In this article I would like to explain how to integrate HTML5 + Javascript in a Windows Phone application and the same demonstrate how to call a .NET method from Javascript and how to call a Javascript method from .NET

So here’s what we need to do to get started:

  1. Create a Windows Phone Silverlight application
  2. Add a WebBrowser component on the main page
  3. Set the IsScriptEnabled property of the WebBrowser component to true
  4. Add an event handler to the ScriptNotify event of the WebBrowser component
  5. Create a folder on the project called HTML and add the HTML, Javascript, and Stylesheet assets to this folder
  6. Write code to copy the HTML related assets to IsolatedStorage
  7. Set the source of the WebBrowser component to the main HTML page

Simple isn’t it?

How it works

The steps above really do seem to be quite simple, and yes it really is. For Javascript to call into the host of the WebBrowser control we can use the window.external.notify() method. This is the same approach for having Javascript code execute code in the host application in other platforms. The window.external.notify() method takes a string which can be used to contain meta data that describes what you want the host to do. And for .NET code to execute Javascript code we use the InvokeScript() method of the WebBrowser control. The InvokeScript() method takes a string parameter that describes the Javascript method to execute, and a collection of strings that describe the arguments to be passed to the Javascript method to execute. If the method that will invoke a javascript function from the host is running on a non-UI thread (worker thread) then the best approach to using this method is by calling InvokeScript(“eval”, “methodName(args1,args2,args3)”) instead of passing the name of the method to be invoked as the first method argument.

Here’s a diagram I used in DDC 2012 that illustrates the process mentioned above:

For this example, we will have an application that hosts a HTML5 page that displays memory information of the device (as shown in the screenshot below)

And here’s the code…

Default.html (HTML5 + Javascript)

The code below is going to be used as a local html file that is to be copied to isolated storage. Let’s put this in a folder called HTML

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=480, height=800, user-scalable=no" />
    <meta name="MobileOptimized" content="width" />
    <meta name="HandheldFriendly" content="true" />
    <title>HTML5 and Windows Phone 7</title>
    <style>
        body
        {
            color: White;
            background-color: Black;
            font-family: 'Segoe WP Semibold';
            text-align: left;
        }
        h3
        {
            font-size: 20pt;
        }
        input
        {
            color: #ffffff;
            background-color: #000000;
            border: 2px solid white;
            vertical-align: baseline;
            font-size: 17pt;
            min-width: 40px;
            min-height: 40px;
            margin: 5;
        }
    </style>
</head>
<body onload="onLoad()">
    <div>
        <h3>
            Current memory usage:</h3>
        <input id="memoryUsage" type="text" value="0" />
        <h3>
            Memory usage limit:</h3>
        <input id="memoryUsageLimit" type="text" value="0" />
        <h3>
            Peak memory usage:</h3>
        <input id="peakMemoryUsage" type="text" value="0" />
        <h3>
            Total memory:</h3>
        <input id="totalMemory" type="text" value="0" />
    </div>
    <script type="text/javascript">
        function onLoad() {
            window.external.notify("getMemoryUsage");
        }

        function getMemoryUsageCallback(memoryUsage, memoryUsageLimit, peakMemoryUsage, totalMemory) {
            document.getElementById("memoryUsage").value = memoryUsage;
            document.getElementById("memoryUsageLimit").value = memoryUsageLimit;
            document.getElementById("peakMemoryUsage").value = peakMemoryUsage;
            document.getElementById("totalMemory").value = totalMemory;
        }
    </script>
</body>
</html>

MainPage.xaml

The code below is the main page of the Silverlight application that will host the HTML content

<phone:PhoneApplicationPage x:Class="PhoneApp.MainPage"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                           xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           mc:Ignorable="d"
                           d:DesignWidth="480"
                           d:DesignHeight="768"
                           FontFamily="{StaticResource PhoneFontFamilyNormal}"
                           FontSize="{StaticResource PhoneFontSizeNormal}"
                           Foreground="{StaticResource PhoneForegroundBrush}"
                           SupportedOrientations="Portrait"
                           Orientation="Portrait"
                           shell:SystemTray.IsVisible="True"
                           Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot"
         Background="Transparent">
        <phone:WebBrowser Name="browser"
                         IsScriptEnabled="True"
                         Source="HTML/Default.html"
                         ScriptNotify="browser_ScriptNotify" />
    </Grid>

</phone:PhoneApplicationPage>

MainPage.xaml.cs

And here’s the code behind the xaml file

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (!store.DirectoryExists("HTML")) store.CreateDirectory("HTML");
            CopyToIsolatedStorage("HTML\\Default.html", store);
        }
    }

    private static void CopyToIsolatedStorage(string file, IsolatedStorageFile store, bool overwrite = true)
    {
        if (store.FileExists(file) && !overwrite)
            return;

        using (Stream resourceStream = Application.GetResourceStream(new Uri(file, UriKind.Relative)).Stream)
        using (IsolatedStorageFileStream fileStream = store.OpenFile(file, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            int bytesRead;
            var buffer = new byte[resourceStream.Length];
            while ((bytesRead = resourceStream.Read(buffer, 0, buffer.Length)) > 0)
                fileStream.Write(buffer, 0, bytesRead);
        }
    }

    private void browser_ScriptNotify(object sender, NotifyEventArgs e)
    {
        var response = new object[]
                       {
                           DeviceStatus.ApplicationCurrentMemoryUsage,
                           DeviceStatus.ApplicationMemoryUsageLimit,
                           DeviceStatus.ApplicationPeakMemoryUsage,
                           DeviceStatus.DeviceTotalMemory
                       };
        browser.InvokeScript("getMemoryUsageCallback", response.Select(c => c.ToString()).ToArray());
    }
}

What happens in the code above is that when the main page has loaded, the html assets are copied to isolated storage and loaded into the web browser component as a local file. When ScriptNotify is triggered, the Silverlight application retrieves memory information using the DeviceStatus class and passes this information back to the WebBrowser component by invoking the getMemoryUsageCallback() method using the InvokeScript() method of the WebBrowser component

The sample above is a very basic and naive but it demonstrates something that can provide endless platform interop possibilities. I hope you found this useful.

You can grab the full source code the sample above here