Corrado's Blog 2.0

Online thoughts of a technology funatic

Dynamic localization of Windows Store apps

Localizing a Windows Store app is a fairly easy task thanks to the x:UId attribute introduced with WinRT.
If you need to create a Multilanguage application just add to your Visual Studio solution a folder named Strings and below it add a folder for each language you want to support using the two letter ISO language as name.
In this picture I have a solution that support Italian and American English

image

Inside each folder I’ve added a resource file and note that there’s a Resource.resw file just below Strings folder, it represent the default fallback resource language file that will be used in case your app will be executed on a system whose language is different than America English or Italian.

Inside each Resource.resw add the property of the control to localize, in our case we want to localize the Text property of a TextBlock, so the file content for each will be:

en-US folder

image

it-IT folder

image

Now to localize the Text property of our TextBlock all we need to to is use this XAML:

1 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 2 <TextBlock x:Uid="WelcomeMessage" 3 VerticalAlignment="Center" 4 Foreground="BlueViolet" 5 Text="..." 6 HorizontalAlignment="Center" 7 FontSize="48" /> 8 </Grid>

Here’s what you’ll see when the code is executed on an Italian pc:

image

Very easy isn’t it? Smile

This approach let you do more than just localize Text, if you want to dig more into it you can read this article

Ok, but imagine you have this mandatory requests:

  • 1-Application language can be unrelated to machine language (French app running on an Italian machine)

2-Language switching should not require an application reboot

3-You must use a MVVM friendly solution

Ok, things can get a little more intricate here  and since I haven’t found a standard solution I solved this way:

Step 1: Since we’ll deal with text only let’s rename resources inside .resw files from WelcomeMessage.Text to just WelcomeMessage (see below)

image

Step 2: Let’s create a ViewModel (yes, MVVM always requires a ViewModel…) and let’s add the necessary code to load the strings dynamically:

1 public class MyViewModel : INotifyPropertyChanged 2 { 3 private ResourceContext resoureContext; 4 5 public MyViewModel(string language) 6 { 7 this.UpdateCulture(language); 8 } 9 10 public void UpdateCulture(string language) 11 { 12 this.resoureContext = ResourceContext.GetForCurrentView(); 13 this.resoureContext.Languages = new List<string> { language }; 14 } 15 16 public string GetResource(string stringResource) 17 { 18 try 19 { 20 var resourceStringMap = ResourceManager.Current.MainResourceMap.GetSubtree("Resources"); 21 return resourceStringMap.GetValue(stringResource, this.resoureContext).ValueAsString; 22 } 23 catch (Exception ex) 24 { 25 return $"?{stringResource}?"; 26 } 27 } 28 29 ... 30 }

The magic here is inside UpdateCulture and GetResource methods, if we name the TextBlock in previous XAML snippet MyTextBlock we can dynamically change its text using this code:

1 public sealed partial class MainPage : Page 2 { 3 public MainPage() 4 { 5 this.InitializeComponent(); 6 this.MyTextBlock.Text = new MyViewModel("it-IT").GetResource("WelcomeMessage"); 7 8 } 9 }

But since we’re using MVVM we want to use databinding for that instead of code and we need a smart solution otherwise localization can become tedious, solution is not very far: just use an indexer:

Just add this line of code to the ViewModel

1 public string this[string key] => this.ApplicationController.GetResource(key);

And let’s inform binding that our indexer property need to be refreshed so that all texts will be reloaded when user changes the UI language at runtime modifying the UpdateCulture method this way:

1 public void UpdateCulture(string language) 2 { 3 this.resoureContext = ResourceContext.GetForCurrentView(); 4 this.resoureContext.Languages = new List<string> { language }; 5 this.OnPropertyChanged("Item[]"); 6 }

Note the trick of using “Item[]” as property name to signal that the class indexer has changed.

So let’s bind TextBlock’s Text property to viewmodel indexer using this syntax, and you’re done.

1 <TextBlock x:Name="MyTextBlock" 2 VerticalAlignment="Center" 3 Foreground="BlueViolet" 4 Text="{Binding [WelcomeMessage]}" 5 HorizontalAlignment="Center" 6 FontSize="48" />

You can find a complete example here.

Have fun!

Scroll with snapping in Windows Phone 8

I’m working on a Windows Phone 8 app that has an “unconventional” UI as it does not follow Windows Phone UX guidelines (it’s a company private app) and so it has home page made up of several screens horizontally aligned that user can pan.

Untitled

I started wrapping the screens inside a horizontally oriented WrapPanel and content, as expected, scrolled.
Mission accomplished? well not really: Content was scrolling but final experience was poor because of missing snapping feature, it was extremely easy lo leave home page with more than one screen visible.

image

To overcome this issue I decides to create a Blend Behavior that attached to a ScrollViewer would take care of smoothly bring into view the correct screen depending on scroll direction.
Solution wasn’t as immediate as previous one since I decided to use an ItemsControl as ScrollViewer’s content since I have the same requirements in other views where content is databound and I had to “dig into” ScrollViewer and attach to both horizontal and vertical scrollbars (yes, I need to support both scrolling direction) but at the end it worked.

Yeah! but, there’s another UX issue: ScrollViewer has the ‘compression’ feature, the one often used to create the famous “Pull to refresh” functionality in lists. Since final experience was not acceptable and since I haven’t found a reliable way of disabling it I abandoned this 2nd solution and decided to go handling all scrolling details.

Enter the SnappingScrollViewer

I won’t bother you with a long code listing (you can get the source code here) but what I did basically was:

  1. -Create a class that inherits from Canvas
  2. -Register Canvas Manipulation events
  3. -Use ManipulationMode.Delta event to set Canvas left/top offset to scroll content
  4. -Use ManipulationMode.Completed event to detect current scrolling position and scroll the screen to proper position using an animation.

Since animation target requires a dependency property and my class is based on Canvas SetLeft/SetTop method I used an old XAML trick adding a custom ScrollOffset dependency property and invoking proper method inside property’s changed event:

protected virtual void OnScrollOffsetChanged(double oldValue, double newValue)
        {
            if (this.IsVertical)
            {
                SetTop(this.content, newValue);
            }
            else
            {
                SetLeft(this.content, newValue);
            }
        }

content is ScrollViewer’s ItemsControl that I get into Loaded event.

private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.content = (ItemsControl)this.Children.First();
            this.totalPages = (int)(this.IsVertical ? (this.content.ActualHeight / this.ItemSize) : (this.content.ActualWidth / this.ItemSize));
            this.snapAmount = this.ItemSize * this.Inertia;
            this.itemIndex = this.InitialIndex;
            if (this.IsVertical)
            {
                SetTop(this.content, -this.InitialIndex * this.ItemSize);
            }
            else
            {
                SetLeft(this.content, -this.InitialIndex * this.ItemSize);
            }
        }

Use is very simple:

<smoothScroller:SnappingScrollViewer x:Name="LayoutRoot"
                                   ItemSize="480"
                                   InitialIndex="2"
                                   IsVertical="False"
                                   Background="White">
        <ItemsControl Background="#FFE01818">
            <ListBoxItem>
                <Grid Background="Aqua"
                      Width="480"
                      Height="800" />
            </ListBoxItem>
            <ListBoxItem>
                <Grid Background="YellowGreen"
                      Width="480"
                      Height="800" />
            </ListBoxItem>

            <ListBoxItem>
                <Grid Background="Orange"
                      Width="480"
                      Height="800" />
            </ListBoxItem>
            <ListBoxItem>
                <Grid Background="Red"
                      Width="480"
                      Height="800" />
            </ListBoxItem>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </smoothScroller:SnappingScrollViewer>

Limitations in sample code: All content must have the same dimension and no support of dynamically added items (did that in production code but wanted to keep demo simple Smile)

Go grab the code and enhance it depending on your needs.

Note for Windows Phone 8.1: ScrollViewer in Windows Phone 8.1 has a HorizontalSnapPointType property that can be used to “snap” content, so this XAML will work as expected

<ScrollViewer HorizontalScrollMode="Enabled"
                      HorizontalScrollBarVisibility="Visible"
                      HorizontalSnapPointsType="Mandatory">
            <StackPanel Orientation="Horizontal">
                <Grid Height="800"
                      Background="#FF1EAA4B"
                      Width="480" />
                <Grid Height="800"
                      Background="#FFDAB611"
                      Width="480" />
                <Grid Height="800"
                      Background="#FF9C0EC5"
                      Width="480" />
            </StackPanel>
        </ScrollViewer>

Unfortunately Compression effect is still present Sad smile if anyone knows how to disable it just let me know, I’m more than interested.

Enjoy!

 

Exception deriving from WebClient on Windows Phone 7.5

I was working on a Windows Phone 7.5 app that requires to use WebClient class to post a HEAD request to the server, since WebClient doesn’t support it, i used the trick to derive from WebClient and override the GetWebRequest method.

public class WebClientEx : WebClient
        {            
            public WebClientEx()
            {
            }
            
            public string Method
            {
                get;
                set;
            }

            protected override WebRequest GetWebRequest(Uri address)
            {
                WebRequest webRequest = base.GetWebRequest(address);

                if (!string.IsNullOrEmpty(Method))
                    webRequest.Method = Method;

                return webRequest;
            }
        }

i then used the new class this way:

WebClientEx client = new WebClientEx() { Method = "HEAD" };
client.DownloadStringCompleted += (s, e) => ...
client.DownloadStringAsync(new Uri(uri, UriKind.Absolute));

Nothing new indeed, so what is the reason of this post? the fact that everything was fine in Windows Phone 8 but it was failing with a “inheritance security rules violated by type WebClientEx. Derived types must either  match the security accessibility of the base type or be less accessible.  If the base class has a non-transparent default constructor, the derived class must also have a default constructor, and the method inheritance rules apply across those two methods.”  when run on a Windows Phone 7.5 device.

A quick search and it looks like that the reason comes from a missing attribute applied to derived class constructor.

Adding a default constructor marked with SecuritySafeCritical attribute solved it (Phew!)

[System.Security.SecuritySafeCritical]
public WebClientEx()
{
}