0 Comments

imageThis post will show you how to use repositioning to create a responsive layout for your XAML Universal Windows app (UWP) using Grid and AdaptiveTrigger.

Background

With Windows 10 and its UWP stack, making your app look nice on both mobile and desktop is one of the core requirements. The Responsive design 101 for Universal Windows Platform (UWP) apps guide on MSDN outlines six different ways for responsive design:

  • Reposition
  • Resize
  • Reflow
  • Reveal
  • Replace
  • Re-architect

In this post we’ll use the familiar Grid-control with the AdaptiveTrigger to reposition our app’s content. On desktops and tablets the app will use 2-column layout. On mobile devices (or when user resizes the app to have a small window) the second column drops under the first one.

AdaptiveTrigger

AdaptiveTrigger is a new addition in Windows 10. You can use AdaptiveTrigger to automatically change the VisualState when the app’s width or height changes. For example the following Channel9-video contains good information about the AdaptiveTrigger.

The code

Here’s out app’s simple layout:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="MainGrid">

        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid x:Name="FirstGrid" Grid.Column="0" Grid.Row="0" Background="#0078d7" />
        <Grid x:Name="SecondGrid" Grid.Column="1" Grid.Row="0" Background="#107C10" />

    </Grid>

Without any VisualStates or AdaptiveTriggers, the app will look the same on all screen sizes:

image

But what we want is to reposition our SecondGrid under the FirstGridon mobile devices. To do this, we need to change the Grid.Column and Grid.Row of the SecondGrid:

                        <Setter Target="SecondGrid.(Grid.Column)" Value="0"/>
                        <Setter Target="SecondGrid.(Grid.Row)" Value="1"/>

Also, there’s no need for two columns so we modify the MainGrid for our needs:

                        <Setter Target="MainGrid.RowDefinitions[1].Height" Value="*"/>
                        <Setter Target="MainGrid.ColumnDefinitions[1].Width" Value="auto"/>

Last thing we do is change the margins to make things little prettier:

                        <Setter Target="MainGrid.Margin" Value="12"/>
                        <Setter Target="FirstGrid.Margin" Value="0 0 0 6"/>
                        <Setter Target="SecondGrid.Margin" Value="0 6 0 0"/>

And that’s it. Here’s all the VisualState changes combined. First the WideState (2-column layout):

                        <Setter Target="MainGrid.Margin" Value="24"/>
                        <Setter Target="MainGrid.RowDefinitions[1].Height" Value="auto"/>
                        <Setter Target="MainGrid.ColumnDefinitions[1].Width" Value="*"/>
                        <Setter Target="FirstGrid.Margin" Value="0 0 6 0"/>
                        <Setter Target="SecondGrid.Margin" Value="6 0 0 0"/>
                        <Setter Target="SecondGrid.(Grid.Column)" Value="1"/>
                        <Setter Target="SecondGrid.(Grid.Row)" Value="0"/>

And then the NarrowState (mobile layout):

                        <Setter Target="MainGrid.Margin" Value="12"/>
                        <Setter Target="FirstGrid.Margin" Value="0 0 0 6"/>
                        <Setter Target="SecondGrid.Margin" Value="0 6 0 0"/>
                        <Setter Target="MainGrid.RowDefinitions[1].Height" Value="*"/>
                        <Setter Target="MainGrid.ColumnDefinitions[1].Width" Value="auto"/>
                        <Setter Target="SecondGrid.(Grid.Column)" Value="0"/>
                        <Setter Target="SecondGrid.(Grid.Row)" Value="1"/>

With these few lines of XAML we have used repositioning to achieve a nice responsive layout.

image

Download source code

The full source code is available from GitHub.

https://github.com/mikoskinen/UWPResponsiveXAMLLayoutGridAdaptiveTrigger

0 Comments

We’re working with a Windows 8.1 Store App where we need to know the physical screen size of the display. Here’s some simple code which seems to do the trick in most cases:

            var width = Window.Current.Bounds.Width * (int)DisplayProperties.ResolutionScale / 100;
            var height = Window.Current.Bounds.Height * (int)DisplayProperties.ResolutionScale / 100;

            var dpi = DisplayInformation.GetForCurrentView().RawDpiY;

            var screenDiagonal = Math.Sqrt(Math.Pow(width / dpi, 2) +
                        Math.Pow(height / dpi, 2));

            return screenDiagonal.ToString();

Please note that the DPI will return 0 if you have two displays in duplicate mode: http://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.display.displayinformation.rawdpix

Portable Class Libraries are a great way to share common code between different platforms. But here lies the danger: A portable class library project cannot change the underlying platform.

Here’s a simple explanation of what that means.

Example project

The example application is built for Windows Forms, Windows RT and Windows Phone. The authentication mechanism is identical in every app so that is placed in a Portable Class Library project:

    public class WebTool
    {
        public async Task<string> GetAuthenticationKey()
        {
            var cookieContainer = new CookieContainer();
            var client = new HttpClient(new HttpClientHandler() { CookieContainer = cookieContainer });

            // Authenticate with the service
            await client.GetStringAsync("http://cookietest.api.domain.com/cookie/get");

            // Return the received cookie as authentication key
            var cookieHeader = cookieContainer.GetCookieHeader(new Uri("http://cookietest.api.domain.com/"));

            return cookieHeader;
        }
    }

Using this library doesn’t get easier: Each platform can call it with identical code:

            var webTool = new WebTool();

            var result = await webTool.GetAuthenticationKey();

            if (string.IsNullOrWhiteSpace(result))
                this.Result.Text = "Didn't receive authentication key!";

            else
                this.Result.Text = "Authentication key: " + result;

Few minutes and everything is done, so the testing phase begins.

First, Windows Forms:

image

Then Windows 8:

image

And last but not least, Windows Phone:

image

Uh oh.

Cookies and CookieContainer especially are different beasts in different platforms. Portable Class Library cannot hide that fact.

Have you encountered similar issues when dealing with Portable Class Library projects?

0 Comments

The regional settings still misbehave in Windows 8.1 XAML apps. They misbehaved already in Windows 8 and they continue to misbehave in apps written with a newer WinRT. Some may say that they work as designed. To them I say the design is wrong.

The previous post about this subject contains more info, but here’s a short introduction to the problem.

Scenario:A Finnish users starts my Windows 8 XAML app. The app displays dates and times. The app isn’t localized to any language. I want the app to show the dates and times in Finnish format as that’s how she has configured her regional settings:

The problem:The app doesn’t respect her settings. Instead, all the dates and times are formatted using the US settings:

Capture

Other frameworks: Windows Forms, Console Applications, WPF, Silverlight and ASP.NET all handle this correctly: Dates and times are formatted using the regional settings:

image

The workaround: It’s possible to get the dates and times formatted correctly in WinRT apps. It’s just requires a lot of code. I call this a workaround until the WinRT XAML apps start behaving as they should:

            var usersLanguage = Windows.Globalization.Language.CurrentInputMethodLanguageTag;

            var dateFormatter = new DateTimeFormatter("shortdate", new[] { usersLanguage }).Patterns[0];
            var timeFormatter = new DateTimeFormatter("shorttime", new[] { usersLanguage }).Patterns[0];
            var fullFormatter = new DateTimeFormatter(dateFormatter + " " + timeFormatter);

            this.MyDate.Text = fullFormatter.Format(DateTime.Now);

image

Update 4.7.2013:

It seems I can get the behavior similar to Silverlight and other frameworks by setting the Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride on application startup (App.xaml.cs). For example:

        public App()
        {
            Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = Windows.Globalization.Language.CurrentInputMethodLanguageTag;
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

 

4 Comments

screenshot_06272013_100012

In this tutorial we’re going to create a simple XAML app using the new Hub control. The tutorial is also going to show how SemanticZoom can be used with the Hub.

The Hub control is one of the new additions in Windows 8.1 XAML SDK and as described by the MSDN, Hub is “a new control for XAML that lets you easily create the hub design pattern that reflects the proper design guidelines and behavior”.

Why to use Hub control?

The MSDN again does a great job of summing up the reasoning behind a hub page and the hub control:

Hub pages are the user''s entry point to your app. They display content in a rich, panning view that lets users get a glimpse of what''s new and exciting, and then dig deeper into your app''s content. The hub displays different categories of content, each of which maps to your app''s section pages. Each section should bubble up content or functionality. The Hub should offer a lot of visual variety, engage users, and draw them in to different parts of the app.

For example the Weather and the Travel apps use hub pages as their starting points:

image

Getting started

In this tutorial we’re going to start from the “Blank App (XAML)” template. The aim is to create a hub page for a movie app using the new control.

screenshot_06272013_100012

So let’s start by creating the new app from the Blank App template. The template contains only a single empty page, MainPage.xaml, which we’re going to start editing:

image

As a test, if we insert the Hub control inside the Grid and set it’s background to Red, we can see that the new control fills the whole layout container:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

        <Hub Background="Red"></Hub>

    </Grid>

image

As we have a working Hub control, it’s time to add some content to it. Let’s start from the header.

Setting the header

The header in Hub control is visible all the time, even when the user scrolls horizontally. It usually includes the app’s name but it can also contain other controls like the back button. The header can be configured using the Header-property:

        <Hub Background="Red">

            <Hub.Header>
                <Grid>

                </Grid>
            </Hub.Header>

        </Hub>

In this tutorial we only want to show the app’s name inside the header:

            <Hub.Header>
                <Grid>
                    <TextBlock Text="The Movie App" />
                </Grid>
            </Hub.Header>

image

As we can see, there’s no need to define style for the TextBlock. The header automatically sets the font’s size and weight and also the padding. Because the only thing we need in the header is the app’s name, we can configure the header even more easily:

        <Hub Background="Red" Header="The Movie App">

        </Hub>

Now that we have the header configured, it’s time add some actual content into the hub.

Adding content into Hub control: The HubSection control

The Hub control itself only shows the header. We need to add HubSection controls inside the Hub control to add the actual content. This is similar to the Windows Phone app’s, where we have the Panorama and the PanoramaItem controls.

In our app, we want to display four hub sections:

  • The second section just displays a static image from a movie
  • The first section shows pictures of the most popular movies
  • The third section list some movies near you
  • The last section provides movie trailers

Let’s add these sections now:

        <Hub Background="Red" Header="The Movie App">
            <HubSection Header="Popular"></HubSection>

            <HubSection></HubSection>

            <HubSection Header="Near You"></HubSection>

            <HubSection Header="Trailers"></HubSection>
        </Hub>

And here’s the result:

image

Let’s add some content into the HubSections, starting from the second one (the one which displays the static movie image).

Full height image in HubSection

We want to display an image in the HubSection. And we want it to use all the available space. If we add the image as a content, this is what happens:

image

The image doesn’t use the whole space. The trick is to set the image as the background of the HubSection:

        <Hub Background="Red" Header="The Movie App">
            <HubSection Header="Popular"></HubSection>

            <HubSection Width="500">
                <HubSection.Background>
                    <ImageBrush ImageSource="/Assets/poster.jpg" Stretch="UniformToFill"/>
                </HubSection.Background>
            </HubSection>

            <HubSection Header="Near You"></HubSection>

            <HubSection Header="Trailers"></HubSection>
        </Hub>

Also notice that we set the Width of the HubSection. Here’s the result:

image

Now it looks much better. One HubSection down, three to go.

Adding content to HubSection

We still have three empty HubSections. But filling them is easy, as we can use the standard GridView and ListView controls. A HubSection can contain any content you like: Buttons, UserControls, Grids, StackPanels etc. But what is little peculiar is that the HubControl doesn’t have a Content-property: You set the content by setting its DataTemplate. Here’s a small example:

            <HubSection Header="Popular">
                <DataTemplate>
                    <StackPanel>
                        <Button Content="Button inside a HubSection"/>
                    </StackPanel>
                </DataTemplate>
            </HubSection>

image

Filling the remaining HubSections with GridViews and ListViews is outside the scope if this tutorial, but the source code shows how it’s done. One important point though: A control which is inside a HubSection cannot be directly accessed from code-behind!

For example, here’s a HubSection which contains a single ListView:

            <HubSection Header="Trailers">
                <DataTemplate>
                    <ListView x:Name="MovieTrailers">

                    </ListView>
                </DataTemplate>
            </HubSection>

Even though we’ve given the ListView a name, we cannot access it from the code-behind:

image

This is because the control is inside a DataTemplate. One easy way to get around this limitation is to hook into the ListView’s Loaded-event. Here’s the XAML:

                    <ListView x:Name="MovieTrailers" Loaded="MovieTrailers_Loaded">

                    </ListView>

And the code behind:

        private void MovieTrailers_Loaded(object sender, RoutedEventArgs e)
        {
            var listView = (ListView)sender;
            listView.ItemsSource = trailers;
        }

Header navigation

Our hub page only shows few trailers so the actual app probably has a dedicated page for the trailers. We want to enable the navigation so that the user can click the “Trailers” header to move forward. This feature is built into the Hub control.

First thing we need to to is to enable the header clicking for the HubSection, by setting its IsHeaderInteractive property to true:

            <HubSection Header="Trailers" Width="550" IsHeaderInteractive="True" >
                <DataTemplate>
                    <ListView Loaded="MovieTrailers_Loaded" 
                              SelectionMode="None" 
                              ItemTemplate="{StaticResource Standard130ItemTemplate}"/>
                </DataTemplate>
            </HubSection>

What’s great is that this automatically adds the navigation indicator to the header:

image

Now to handle the click, we must attach to Hub’s (not HubSection’s) SectionHeaderClick-event:

<Hub Header="The Movie App" Background="{StaticResource BackgroundBrush}" SectionHeaderClick="Hub_SectionHeaderClick">

The clicked HubSection can be read from the event’s arguments:

        private void Hub_SectionHeaderClick(object sender, HubSectionHeaderClickEventArgs e)
        {
            var clickedSection = e.Section;
            if (clickedSection.Name == "TrailersSection")
            {
                // Add navigation logic
            }
        }

Semantic zoom

Last thing we’re going to add to the hub is semantic zoom. The Hub control supports semantic zoom, meaning it can be used as a Zoomed in view.

First thing we need to do is to add the SemanticZoom control to the page:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <SemanticZoom x:Name="Zoom">
            <SemanticZoom.ZoomedInView></SemanticZoom.ZoomedInView>

            <SemanticZoom.ZoomedOutView></SemanticZoom.ZoomedOutView>
        </SemanticZoom>

        <Hub x:Name="Hub" Header="The Movie App" Background="{StaticResource BackgroundBrush}" SectionHeaderClick="Hub_SectionHeaderClick">

Then we move the whole Hub inside the ZoomedInView:

image

Also we need a ZoomedOutView. For that, let’s create a simple GridView:

            <SemanticZoom.ZoomedOutView>

                <GridView VerticalAlignment="Center" Margin="200,0,0,0" Loaded="GridView_Loaded">
                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel >
                                <TextBlock HorizontalAlignment="Center" Text="{Binding}" Style="{StaticResource SubheaderTextBlockStyle}" />
                            </StackPanel>
                        </DataTemplate>
                    </GridView.ItemTemplate>

                    <GridView.ItemContainerStyle>
                        <Style TargetType="GridViewItem">
                            <Setter Property="Margin" Value="4" />
                            <Setter Property="Padding" Value="5" />
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                            <Setter Property="VerticalContentAlignment" Value="Center" />
                        </Style>
                    </GridView.ItemContainerStyle>

                    <GridView.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid ItemWidth="400" ItemHeight="70" Orientation="Horizontal" VerticalChildrenAlignment="Center" MaximumRowsOrColumns="3"></WrapGrid>
                        </ItemsPanelTemplate>
                    </GridView.ItemsPanel>
                </GridView>

            </SemanticZoom.ZoomedOutView>

Only one small thing left: We have to provide some items for this GridView. We do that in the GridView_Loaded:

        private void GridView_Loaded(object sender, RoutedEventArgs e)
        {
            var sections = this.Hub.Sections;
            var headers = new List<string>();

            foreach(var item in sections)
            {
                var section = (HubSection)item;
                var header = (string)section.Header;
                if (string.IsNullOrWhiteSpace(header))
                    continue;

                headers.Add(header);
            }

            ((GridView)this.Zoom.ZoomedOutView).ItemsSource = headers;
        }

The code loops through all the HubSections. It takes the sections header from each of them and add its to the “headers”-list. This list is then set as the ItemsSource for our ZoomedOutView’s GridView. And that’s it. We can now zoom out:

image

And click any of the items to zoom into that section.

Final touches and the source code

At this point we’ve realized that the app looks better if we put the static picture as the first HubSection. Also, the red background can be replaced with a little less stressful color.

Full source code for the tutorial is available from GitHub (folder hubcontrol-getting-started). The app works with the preview release of Visual Studio 2013 and Windows 8.1.