Diederik Krols

The XAML Brewer

A lap around Adaptive Triggers

When you’re building a XAML app for the Universal Windows Platform (UWP) you’ll end up with just one package to be deployed to all the different Windows 10 devices. They’re all served with the same C# and the same XAML views. Fortunately XAML has been extended with a load of features to support adaptive/responsive design:

  • Native controls that adapt to the device,
  • the new RelativePanel control,
  • a new common navigation UI with the SplitView control,
  • extensions to the Visual State infrastructure with Adaptive Triggers and Property Setters, and as a last resort
  • XAML views per device.

I already covered the RelativePanel and SplitView controls, so let’s dive into Adaptive Triggers.

The new AdaptiveTrigger class is used in XAML to describe the conditions to move to a visual state. At runtime, the adaptive triggers are evaluated, and the most appropriate will determine the visual state. All of this happens without any code behind from your part.

An adaptive trigger comes with two properties: the minimum width and height of the app’s window to trigger the state change: MinWindowHeight and MinWindowWidth.

Here’s the list of trigger declarations from the small sample app that I wrote. I defined four visual states:

  • Wide: larger than 900 effective pixels,
  • Square: between 720 and 899 effective pixels,
  • Narrow: between 548 and 719 effective pixels, and
  • Snapped: less than 548 effective pixels.
<!-- Responsive Visual States -->
<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>

        <!-- VisualState to be triggered when window width is >=900 effective pixels -->
        <VisualState x:Name="Wide">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="900" />
            </VisualState.StateTriggers>
          
            <!-- Setters and Storyboards come here -->
            <!-- ... -->          

        </VisualState>

        <!-- VisualState to be triggered when window width is >=720 and <900 effective pixels -->
        <VisualState x:Name="Square">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="720" />
            </VisualState.StateTriggers>

            <!-- Setters and Storyboards come here -->
            <!-- ... -->  
        
        </VisualState>
                
        <!-- VisualState to be triggered when window width is >=548 and <720 effective pixels -->
        <VisualState x:Name="Narrow">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="548" />
            </VisualState.StateTriggers>

            <!-- Setters and Storyboards come here -->
            <!-- ... -->          
        
        </VisualState>

        <!-- VisualState to be triggered when window width is >=0 and <548 effective pixels -->
        <VisualState x:Name="Snapped">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="0" />
            </VisualState.StateTriggers>

            <!-- Setters and Storyboards come here -->
            <!-- ... -->          
    
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Of course it’s up to you to define your own boundaries. These depend on your app and on the devices that you target with it.

In the pre-UWP world, the content of a Visual State was one Storyboard that was executed when the control entered the corresponding state - generally through a call to VisualStateManager.GoToState in code behind. The UWP Visual State also comes with an optional storyboard, but also with a list of setters. That’s the same Setter that you know from within a Style definition. Basically it modifies the value of a property, without animation. The Setter is extended with a new property -Target- that contains the path to the property that you want to update, including the target element. It also works for any type of property, including attached properties.

If you had a hamburger button in your UI and wanted to hide it in a particular visual state, then this would do the trick:

<Setter Target="HamburgerButton.Visibility" Value="Collapsed" />

Which brings us to the sample app, which has a Shell with a SplitView, and a MainPage – reused from my SplitView sample.

 

Applying Visual States to a SplitView control

In the Wide state, I’m showing the splitview panel in full width and in Inline mode – not overlapping the content. The hamburger button does not add any value in that case, so I’m hiding it:

<!-- VisualState to be triggered when window width is >=900 effective pixels -->
<VisualState x:Name="Wide">
    <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="900" />
    </VisualState.StateTriggers>
    <VisualState.Setters>
        <!-- Keep SplitView pane always showing inline -->
        <!-- Hide the Hamburger, it's useless here -->
        <Setter Target="MySplitView.DisplayMode"
                Value="Inline" />
        <Setter Target="MySplitView.IsPaneOpen"
                Value="True" />
        <Setter Target="HamburgerButton.Visibility"
                Value="Collapsed" />
    </VisualState.Setters>
</VisualState>

The shell looks like this in Wide mode:

SplitView_1000

For the Square state, we just switch to Overlay mode. That saves 150 effective pixels. I also try to keep the pane open, but it collapses automatically on any screen resize, and there does not seem to be a way to keep it sticky. In overlay mode the user would want to close and reopen the menu, so we bring the hamburger button back. We don’t have to do anything for that: the adaptive trigger nicely undoes all the setters it executed. So we only need to update the splitview:

<VisualState.Setters>
    <!-- SplitView pane shows as overlay -->
    <!-- Splitview pane will close however :-( -->
    <Setter Target="MySplitView.DisplayMode"
            Value="Overlay" />
    <Setter Target="MySplitView.IsPaneOpen"
            Value="True" />
</VisualState.Setters>

And here’s the result:

SplitView_0850

Here are the setters for Narrow mode, we switch to Compact Inline mode, and keep the pane open:

<Setter Target="MySplitView.DisplayMode"
        Value="CompactInline" />
<Setter Target="MySplitView.IsPaneOpen"
        Value="False" />

Unsurprisingly, this is the result:

SplitView_0650

Al last we switch to back to Overlay mode, but we close the pane. I guess you have an idea now how the setters look like.

To allow the window to be resized to such a small size, I’ve overridden the minimum size in App.cs:

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    // Override default minimum size.
    var view = ApplicationView.GetForCurrentView();
    view.SetPreferredMinSize(new Size { Width = 320, Height = 320 });
        
    // ...
}

Here’s the narrow screen:

SplitView_0350

 
Applying Visual States to a Relative Panel control

In the MainPage of the sample app I defined the same visual states and adaptive triggers as in the Shell. I’m using Setters to update regular properties:

<Setter Target="LinksPanel.Visibility"
        Value="Collapsed" />
<Setter Target="Title.FontSize"
        Value="36" />
<Setter Target="Title.Padding"
        Value="4" />

But I’m also moving elements around by updating the properties that were attached to them by the RelativePanel, using the parentheses syntax:

<Setter Target="Title.(RelativePanel.AlignLeftWith)"
        Value="Illustration" />
<Setter Target="Title.(RelativePanel.AlignBottomWith)"
        Value="Illustration" />
<Setter Target="Title.(RelativePanel.RightOf)"
        Value="" />
<Setter Target="Title.(RelativePanel.AlignTopWith)"
        Value="" />

The last two setters actually remove attached properties. That’s to avoid circular references in the elements’ positioning and to avoid concurrent alignments (more details in my Relative panel sample).

You’re not just stuck to the new setters inside a visual state, there’s still an optional storyboard available for the property changes that can be animated. Here’s an example of a full visual state from the sample app:

<VisualState x:Name="Narrow">
  <VisualState.StateTriggers>
    <AdaptiveTrigger MinWindowWidth="548" />
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- Make illustration smaller, place title in it, and description at the bottom. -->
    <!--<Setter Target="Illustration.MaxWidth"
                        Value="300" />-->
    <Setter Target="Illustration.Margin"
            Value="0" />
    <Setter Target="Title.(RelativePanel.AlignLeftWith)"
            Value="Illustration" />
    <Setter Target="Title.(RelativePanel.AlignBottomWith)"
            Value="Illustration" />
    <Setter Target="Title.(RelativePanel.RightOf)"
            Value="" />
    <Setter Target="Title.(RelativePanel.AlignTopWith)"
            Value="" />
    <Setter Target="Title.FontSize"
            Value="36" />
    <Setter Target="Title.Padding"
            Value="4" />
    <Setter Target="Description.(RelativePanel.Below)"
            Value="Illustration" />
  </VisualState.Setters>
  <Storyboard>
    <DoubleAnimation Storyboard.TargetProperty="(FrameworkElement.MaxWidth)"
                      Storyboard.TargetName="Illustration"
                      Duration="0:0:0.5"
                      To="300"
                      EnableDependentAnimation="True" />
  </Storyboard>
</VisualState>
 
Defining Visual States in Blend

The new Blend 2015 will be an excellent tool for previewing and recording visual states. On the left you can see and modify the list the states, the center is a preview of the state, and at the right you can modify the properties that define the state:

BlendExperience

At the time of writing this article, Blend 2015 RC is not yet entirely ready for this. All property changes are recorded in the storyboard (not in setters), the attached properties don’t show up in the properties to the right. And if you already had setters in a visual state, they will be ruined. Blend apparently has an other idea of the Setter class than Visual Studio: it will remove your Target properties, and add an empty PropertyPath property. I’m assuming that this problem will disappear in –literally- just a couple of days from now.

 

The final results

Here’s how the sample app elegantly scales down.

Wide mode:

MainView_1000

Square mode:

MainView_0850

Narrow mode:

MainView_0650

 Snapped mode:

MainView_0350

In case you didn’t notice: I didn’t have to write a single line of C# for this, it’s all in the XAML.

 
What’s next ?

The current adaptive triggers only define a visual state by the height and width of the app’s window. Microsoft has promised to enhance the class or release more trigger types. If you can’t wait for this, take a look at the rich collection of Custom Visual State Triggers by Morten Nielsen or start rolling your own. After all, it’s just a matter of inheriting from StateTriggerBase and implementing its SetActive method.

 
Code

The source of the sample app is on GitHub. It was written with Visual Studio 2015 RC and Blend 2015 RC on a Windows 10 fast ring tablet.

Enjoy!

XAML Brewer

A lap around the RelativePanel Control

The RelativePanel is a new XAML layout control for the Universal Windows Platform (UWP). It allows you to decorate its elements with attached properties to position them relative to each other, and align them relative to each other or to the panel itself. That allows for a flexible graph-like layout instead of the current strictly tabular or list layouts.

Here’s the list of new attached properties:

  • Positioning of an element relative to another:
    • Above
    • Below
    • LeftOf
    • RightOf
  • Alignment of an element with another:
    • AlignBottomWith
    • AlignHorizontalCenterWith
    • AlignLeftWith
    • AlignRightWith
    • AlignTopWith
    • AlignVerticalCenterWith
  • Alignment of an element with the panel itself:
    • AlignBottomWithPanel
    • AlignHorizontalCenterWithPanel
    • AlignLeftWithPanel
    • AlignRightWithPanel
    • AlignTopWithPanel
    • AlignVerticalCenterWithPanel

It’s pretty clear what these properties stand for, so let’s dive into code.

The basics

I created a small sample app around this new panel. Here’s how it looks on startup:

RelativePanel_1

Cute, but … nothing spectacular, right?

Indeed any XAML layout or items control can draw this: Grid, StackPanel, ListView, … you name it. True, but these controls would require you to do an upfront design and configuration step where you define the number of rows and columns, orientation, and data templates. A RelativePanel doesn’t require this.

Here’s what I had in mind for the layout in the sample app:

  • Draw an empty relative panel
  • Draw Bob
  • Draw Kevin to the right of Bob
  • Align the images at their bottom
  • Draw Stuart to the right of Kevin
  • Align the images at their bottom
  • Draw Bob’s name under the image
  • Center the name under the image
  • Repeat the previous two steps for Kevin and Stuart

And this is the corresponding declarative XAML:

<RelativePanel x:Name="MinionPanel">

    <!-- Images -->
    <Image x:Name="Bob"
           Source="Assets/Bob.png" />
    <Image x:Name="Kevin"
           Source="Assets/Kevin.png"
           RelativePanel.RightOf="Bob"
           RelativePanel.AlignBottomWith="Bob" />
    <Image x:Name="Stuart"
           Source="Assets/Stuart.png"
           RelativePanel.RightOf="Kevin"
           RelativePanel.AlignBottomWith="Kevin" />

    <!-- Labels -->
    <TextBlock Text="BOB"
               RelativePanel.Below="Bob"
               RelativePanel.AlignHorizontalCenterWith="Bob" />
    <TextBlock Text="KEVIN"
               RelativePanel.Below="Kevin"
               RelativePanel.AlignHorizontalCenterWith="Kevin" />
    <TextBlock Text="STUART"
               RelativePanel.Below="Stuart"
               RelativePanel.AlignHorizontalCenterWith="Stuart" />
</RelativePanel>

Very straightforward, right? Well, there are a couple of things to consider.

The pitfalls

Circular dependencies

Under the hood, the layout algorithm builds up a directed graph (I guess). That graph doesn’t allow direct or indirect circular dependencies. The following definition would create such a dependency (I aligned Bob to Stuart, instead of Stuart to Kevin:

<Image x:Name="Bob"
        Source="Assets/Bob.png" 
        RelativePanel.AlignBottomWith="Stuart"/>
<Image x:Name="Kevin"
        Source="Assets/Kevin.png"
        RelativePanel.RightOf="Bob"
        RelativePanel.AlignBottomWith="Bob"/>
<Image x:Name="Stuart"
        Source="Assets/Stuart.png"
        RelativePanel.RightOf="Kevin" />

So “X AlignWith Y” may behave different than “Y AlignWith X”, and “X LeftOf Y” does never imply “Y RightOf X” (that would definitely create a direct circular dependency).

When you create a circular dependency in XAML, the Visual Studio designer will warn you. It’s not an error however: your code will compile, start to run, and then crash…

Elements not showing

Elements that don’t have the attached properties filled out, will be placed at the upper left corner of the panel. If you use these as anchor, some elements might be pushed out of the panel.

My first idea for the sample app layout was:

  • Draw Kevin (after all he’s the leader)
  • Draw Bob to the left of him
  • Draw Stuart to the right of him

This was my initial XAML:

<Image x:Name="Kevin"
       Source="Assets/Kevin.png"/>
<Image x:Name="Bob"
       Source="Assets/Bob.png"
       RelativePanel.LeftOf="Kevin"/>
<Image x:Name="Stuart"
       Source="Assets/Stuart.png"
       RelativePanel.RightOf="Kevin" />
This draws Kevin at the left side of the panel, and pushes poor Bob out of it. [And it took me way to long to figure out what was happening here.]
Conflicting relationships

When you define conflicting relationships, the panel alignment relationships (AlignTopWithPanel, AlignLeftWithPanel, …) have priority over the element alignment relationships (AlignTopWith, AlignLeftWith, …), which in turn have priority over the element positional relationships (Above, Below, RightOf, LeftOf). The following example has conflicting properties for Kevin; I defined him at the left of the panel, but to the right of Bob:

<Image x:Name="Bob"
       Source="Assets/Bob.png" />
<Image x:Name="Kevin"
       Source="Assets/Kevin.png"
       RelativePanel.RightOf="Bob"
       RelativePanel.AlignLeftWithPanel="True" />
<Image x:Name="Stuart"
       Source="Assets/Stuart.png"
       RelativePanel.RightOf="Kevin" />

The AlignLeftWithPanel wins, so Bob is again pushed out of the panel.

My 2 cents

Defining a layout based on the RelativePanel does require some upfront design. Here are some tricks that may help you:

  • Consider the UI inside the panel as a set of directed graphs that each start with an anchor element.
  • If appropriate, align the anchors with the panel itself.
  • Align the non-anchor elements (graph nodes) relative to each other, not to the panel.
  • Make a simple drawing of the positional and alignment relationships to detect cycles.

Here’s the final layout of the sample app. I have two nodes, the ‘Boo’ button –more on that later- and the XAML Brewer logo. Both are aligned with the panel. All minion elements are positioned (red arrows) and aligned (blue dotted lines) relative to each other and to the button, but never to the panel:

RelativePanel_1b

The true power

The static layout of a RelativePanel’s elements can be easily achieved by other panels and controls. But the RelativePanel really shines at runtime, when you start modifying the graphs. Let’s put some code behind the ‘Boo’ button to insert a new element right between Kevin and Stuart.

In all XAML platforms, attached properties can be programmatically assigned by static Set* methods, and UWP is not an exception. The RelativePanel class comes with methods such as SetAbove, SetAlignLeftWithPanel, SetAlignTopWith and so on. Here’s the logic to add a new image to the graph:

  • Create the image
  • Define all graph edges that start from the image
  • Add the image to the graph
  • Relay all edges that arrive in the new image

This is the corresponding C# code:

// Create Boo
var boo = new Image()
{
    Source = new BitmapImage(new Uri("ms-appx:/Assets/Boo.png")),
    Width = 300,
    Tag = "Boo"
};

// Prepare Boo
RelativePanel.SetRightOf(boo, Kevin);
RelativePanel.SetAlignBottomWith(boo, Kevin);

// Add Boo
MinionPanel.Children.Add(boo);

// Relay the graph edge
RelativePanel.SetRightOf(Stuart, boo);

That last statement can only be executed after the new image was added to the panel, since the panel does not allow you to define relationships towards elements outside of it.

This is the result:

RelativePanel_2

Again, this is cute. But this time it's also spectacular. If you programmatically add a column in the middle of an existing grid, you would be forced to modify all column widths, and the Column attached property on everything to the right of Boo (including Stuart’s name field). Not really flexible.

For the sake of completeness, here’s the code to remove Boo from the Panel. RelativePanel also comes with static Get* methods to retrieve the value of its attached properties, such as GetRightOf:

// Check if Stuart is right of Boo.
var theRightStuff = RelativePanel.GetRightOf(Stuart);
if ((theRightStuff as Image)?.Tag?.ToString() == "Boo")
{
    // Reconnect Stuart to Kevin.
    RelativePanel.SetRightOf(Stuart, Kevin);
    // Remove Boo
    MinionPanel.Children.Remove((UIElement)theRightStuff);
}

Last but not least, the attached properties of RelativePanel can also be modified at runtime through the new UWP Adaptive Triggers, to create a responsive design. But I’m keeping that for a next blog post.

The code

The code for the sample app is on GitHub. It was written with Visual Studio Enterprise 2015 RC on a Windows 10 fast ring box.

Enjoy!

XAML Brewer

A lap around the SplitView Control

The SplitView is a new XAML control that provides a top-level navigation service to apps that target the Universal Windows 10 Platform (UWP). It’s an empty container with room for two views:

  • The Pane hosts the menu items. It can be docked to the left or to the right. It can be opened to its full width (displaying icons and labels), entirely collapsed, or collapsed to a compact mode (displaying only icons). This is controlled through the DisplayMode and IsPaneOpen properties.
  • The Content displays the main content of your page. It is always shown, but can be partly covered by the Pane.

I built a sample app that contains several pages, and uses a menu-like SplitView to navigate between these. The SplitView pane can also host non-navigational menu items. Here’s how the sample app looks like:

PageTwo

Here is the project’s as-light-as-possible MVVM structure:

SplitViewProject

Main Page Structure

The app’s start-up page is just a Shell, so that’s how it’s called - like in classic Prism. It has a SplitView with the menu infrastructure in its Pane, and an empty Frame as its Content to provide navigation support between the app’s pages. Here’s how the basic XAML looks like:

<SplitView x:Name="MySplitView"
            IsPaneOpen="False"
            CompactPaneLength="40"
            OpenPaneLength="150"
            PaneBackground="{StaticResource JustOneShadeOfGray}"
            DisplayMode="Overlay">
    <SplitView.Pane>
        <Grid x:Name="SplitViewPane"
              VerticalAlignment="Stretch"
              HorizontalAlignment="Stretch"
              Background="{StaticResource JustOneShadeOfGray}">
            <ListView ItemsSource="{Binding Menu}"
                      SelectionChanged="Menu_SelectionChanged"
                      Margin="0 48 0 0"
                      VerticalAlignment="Top">
            </ListView>
        </Grid>
    </SplitView.Pane>
    <SplitView.Content>
        <Grid>
            <Frame x:Name="SplitViewFrame">
            </Frame>
        </Grid>
    </SplitView.Content>
</SplitView>

Menu Layout

I created a little helper class for menu items, with Label, Glyph, DestinationType (for navigational items), and Command (for other items) properties:

class MenuItem : BindableBase
{
    private string glyph;
    private string text;
    private DelegateCommand command;
    private Type navigationDestination;

    public string Glyph
    {
        get { return glyph; }
        set { SetProperty(ref glyph, value); }
    }

    public string Text
    {
        get { return text; }
        set { SetProperty(ref text, value); }
    }

    public ICommand Command
    {
        get { return command; }
        set { SetProperty(ref command, (DelegateCommand)value); }
    }

    public Type NavigationDestination
    {
        get { return navigationDestination; }
        set { SetProperty(ref navigationDestination, value); }
    }

    public bool IsNavigation
    {
        get { return navigationDestination != null; }
    }
}

To display these menu items, I'm using a ListView with a DataTemplate that looks nice in both full and compact state:

<ListView.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <FontIcon x:Name="Glyph"
                      Glyph="{Binding Glyph}"
                      VerticalAlignment="Center"
                      HorizontalAlignment="Center"
                      Foreground="White"
                      ToolTipService.ToolTip="{Binding Text}" />
            <TextBlock Text="{Binding Text}"
                        Margin="8 0 0 0"
                        Foreground="White" />
        </StackPanel>
    </DataTemplate>
</ListView.ItemTemplate>

The screenshot above shows the fully expanded menu, this is how the compact version looks like:

PageOne

The look-and-feel of the SplitView Pane, and how it is implemented, is entirely up to you. A ListView is not mandatory: this project by Jerry Nixon uses templated and view state managed radio buttons. Just make sure to follow the navigation design basics for UWP apps in general and the guidelines for navigation panes in particular.

Populating the Menu

The Menu itself is implemented as a Singleton in ViewModelBase:

class ViewModelBase : BindableBase
{
    private static ObservableCollection<MenuItem> menu = new ObservableCollection<MenuItem>();

    public ObservableCollection<MenuItem> Menu {
        get { return menu; }
    }
}

In a real-world MVVM app you would probably expose it through some kind of Service.

Here’s how the ShellViewModel adds all navigation items:

class ShellViewModel : ViewModelBase
{
    public ShellViewModel()
    {
        // Build the menu
        Menu.Add(new MenuItem() { Glyph = "", Text = "First Page", NavigationDestination = typeof(FirstPage) });
        Menu.Add(new MenuItem() { Glyph = "", Text = "Second Page", NavigationDestination = typeof(SecondPage) });
        Menu.Add(new MenuItem() { Glyph = "", Text = "Third Page", NavigationDestination = typeof(ThirdPage) });
    }
}

The icons are taken from the new Segoe MDL2 Assets font:

Segoe_MDL2_Assets

Here’s how a View can add and remove a non-navigational command that is defined in its ViewModel:

public sealed partial class ThirdPage : Page
{
    private MenuItem myItem;

    public ThirdPage()
    {
        this.InitializeComponent();

        myItem = new MenuItem() { Glyph = "", Text = "Conclusion", Command = (DataContext as ThirdPageViewModel).ThirdPageCommand };
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        (this.DataContext as ViewModelBase).Menu.Add(myItem);
        base.OnNavigatedTo(e);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        (this.DataContext as ViewModelBase).Menu.Remove(myItem);
        base.OnNavigatedFrom(e);
    }
}

We're using the OnNavigatedTo and OnNavigatedFrom events to ensure that the corresponding menu items are only displayed when the page is in the frame:

PageThree

In a real-world MVVM app this would look more like this:

MenuService.Register(NavigateToPage3Command);
MenuService.Register(MyCustomCommand);

There’s nothing in the SplitView that prevents such an approach.

Menu Item Execution

Menu item execution is triggered by the ShellViewModel, when the selection in the menu listview was changed:

private void Menu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count > 0)
    {
        var menuItem = e.AddedItems.First() as MenuItem;
        if (menuItem.IsNavigation)
        {
            SplitViewFrame.Navigate(menuItem.NavigationDestination);
        }
        else
        {
            menuItem.Command.Execute(null);
        }
    }
}

Navigation in a real-world MVVM application would probably look more like this:

NavigationService.NavigateTo(type);

… and that’s exactly what you would code when using Template10 – a set of brand-new design-pattern-driven Visual Studio project templates.

Here's the result of the non-navigational command execution in the sample app. The ViewModel updates the UI by updating a data-bound property:

PageThree_2

Opening and Closing the Pane

The SplitView itself contains no infrastructure to open or close the menu Pane, you have to provide that yourself. I see two options here, and they’re not mutually exclusive: the ubiquitous Hamburger button, and a swipe movement.

Hamburger Button

The obvious way to open and close the SplitView’s Pane would be … a Hamburger button. So I added one, not to the Pane itself, but directly in the Shell. This makes sure that the button always stays visible, even when the Pane is closed:

FirstPage_ClosedPane

Here’s the button's XAML, it’s always on top of everything:

<Grid>
  <SplitView x:Name="MySplitView" />

  <!-- Hamburger Button -->
  <Button x:Name="HamburgerButton"
          FontFamily="Segoe MDL2 Assets"
          Content="&#xE700;"
          Width="40"
          Height="40"
          Background="Transparent"
          Foreground="White"
          VerticalAlignment="Top"
          BorderThickness="0"
          Click="HamburgerButton_Click" />
</Grid>

And here’s its event handler:

private void HamburgerButton_Click(object sender, RoutedEventArgs e)
{
    MySplitView.IsPaneOpen = !MySplitView.IsPaneOpen;
}

Swipe

‘Other platforms’ allow you to open the main menu by swiping in from the left (assuming that the menu is docked to the left). In Windows 10 this manipulation is already used for opening the Task View.

But any manipulation that starts inside your view, can be captured. So here’s my alternative opening/closing procedure:

  • To open the collapsed menu, put a finger on where it’s supposed to be (left of right side of the screen) and swipe horizontally in the direction of the center of the screen.
  • To close the menu again, put your finger on it and swipe horizontally toward the (closest) edge of the screen.

I implemented this, and it actually feels very natural.

Here’s how both manipulations are defined – closing from the Pane, opening from the Content:

<SplitView x:Name="MySplitView">
  <SplitView.Pane>
    <Grid x:Name="SplitViewPane"
          ManipulationMode="TranslateX"
          ManipulationCompleted="SplitViewPane_ManipulationCompleted"
          Background="{StaticResource JustOneShadeOfGray}">
      <ListView />

    </Grid>
  </SplitView.Pane>
  <SplitView.Content>
    <Grid>
      <!-- Navigation Frame -->
      <Frame x:Name="SplitViewFrame" />
      <!-- SplitViewOpener -->
      <Grid ManipulationMode="TranslateX"
            ManipulationCompleted="SplitViewOpener_ManipulationCompleted"
            Width="150"
            Background="Transparent"
            HorizontalAlignment="Left"
            VerticalAlignment="Stretch">
      </Grid>
    </Grid>
  </SplitView.Content>
</SplitView>

And here are the handlers – for a Pane that is docked on the left side:

private void SplitViewOpener_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
    if (e.Cumulative.Translation.X > 50)
    {
        MySplitView.IsPaneOpen = true;
    }
}

private void SplitViewPane_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
    if (e.Cumulative.Translation.X < -50)
    {
        MySplitView.IsPaneOpen = false;
    }
}

When going for the swipe solution, you need to visually indicate to the user that there is a hidden menu (remember the Windows 8 days where users didn't find the app bar or the charms). The best way to do that is … by keeping that Hamburger button on the screen. After all, it’s just three thin lines. As I mentioned: swipe and hamburger button are not mutually exclusive. I would even say: "swipe and hamburger button: better together".

What about the phone?

Good question, I’m glad you asked it :-). Well: it should work the same. Unfortunately I’m currently working on a HW/OS/VS/SDK/Emulators combination that doesn’t allow me to successfully run the new phone emulators, so I can't verify this.

What about pre-Windows-10 platforms?

If you like the SplitView concept, but you’re building apps for Windows 8.*, here’s some good news. There is a straight backport of the SplitView to the 8.* Runtimes & Silverlight. You could also go for the Navigation Drawer Layout for Windows Phone, which supports slide-to-open.

The Code

The code for the sample app is available on GitHub. It's focused on a SplitView with a left-docked Pane in regular (non-compact) overlay mode.

Enjoy,

XAML Brewer

A pull-to-refresh ListView for Windows 8.1 Universal Apps

This article presents a Universal Windows 8.1 XAML ListView control with pull-to-refresh capabilities, inspired by the ones in the Twitter and Facebook apps. It’s a straightforward port of the pull-to-refresh ScrollViewer control by Deani Hansen. The PullToRefreshListView comes with the following characteristics:

  • PullText (dependency property) : the text that appears when the user pulls down the ListView
  • RefreshText(dependency property) : the text that appears when the ListView is ready to refresh
  • RefreshHeaderHeight(dependency property) : the height of the header that contains the texts and the arrow
  • RefreshCommand(dependency property) : the command that triggers the refresh
  • ArrowColor(dependency property) : the color of the rotating arrow in the header
  • RefreshContent (event) : the event that triggers the refresh

This snapshot from the sample app shows the ListView in action on a Windows phone:

Pull2RefreshListView

Here’s how it is defined in the XAML page:

<controls:PullToRefreshListView ItemsSource="{Binding Episodes}"
                                RefreshCommand="{Binding RefreshCommand}"
                                ArrowColor="{StaticResource MagooShirtGreen}">
    <controls:PullToRefreshListView.ItemTemplate>
        <DataTemplate>
            <!-- Item template details -->
            <!-- ... -->
        </DataTemplate>
    </controls:PullToRefreshListView.ItemTemplate>
</controls:PullToRefreshListView>

Visual Studio 2015 RC did not allow me to create a copy from the default ListView template through the designer. So I opened the core generic.xaml from C:\Program Files (x86)\Windows Kits\8.1\Include\winrt\xaml\design and took it from there. The heart of the default ListView control is a ScrollViewer, so it was not hard to blend Deani’s pull-to-refresh ScrollViewer in it.

Here’s the XAML template structure: the ItemsPresenter that displays the ListView items is decorated with a header that displays the ‘pull to refresh’ and ‘release to refresh’ texts, and the rotating arrow:

Pull2RefreshTemplate 

Apart from the change of base class from ContentControl to ListView, I did not have to change a lot in the original source code. The principle is still the same. The new header is hidden by giving the entire control a negative margin:

protected override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    scrollViewer = (ScrollViewer)GetTemplateChild(ScrollViewerControl);
    scrollViewer.ViewChanging += ScrollViewer_ViewChanging;
    scrollViewer.Margin = new Thickness(0, 0, 0, -RefreshHeaderHeight);
    var transform = new CompositeTransform();
    transform.TranslateY = -RefreshHeaderHeight;
    scrollViewer.RenderTransform = transform;

    containerGrid = (Grid)GetTemplateChild(ContainerGrid);
    pullToRefreshIndicator = (Border)GetTemplateChild(PullToRefreshIndicator);
    SizeChanged += OnSizeChanged;
}

That negative margin pushes the header above the control’s area, but still visible. The control needs to be clipped to make the header disappear:

private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    Clip = new RectangleGeometry()
    {
        Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)
    };
}

The pull-to-refresh behavior is implemented by two timers:

  • One timer detects if the user is trying to pull the control down, and if he has passed a threshold in pixels.
  • The second timer detects if the user has passed that pulling threshold for a long enough time.

The timers make sure that the –possibly expensive- refresh operation is not triggered accidentally. They only run when the control is at the top of the list (i.e. when it has no VerticalOffset):

private void ScrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
    if (e.NextView.VerticalOffset == 0)
    {
        timer.Start();
    }
    else
    {
        // reset everything
        // ...
    }
}

The first timer constantly verifies if the pulling pixel threshold is reached. If that’s the case, the second timer is started. Only when both timers were fired, the actual refresh is invoked:

private void Timer_Tick(object sender, object e)
{
    if (containerGrid != null)
    {
        Rect elementBounds = pullToRefreshIndicator.TransformToVisual(containerGrid).TransformBounds(new Rect(0.0, 0.0, pullToRefreshIndicator.Height, RefreshHeaderHeight));
        var compressionOffset = elementBounds.Bottom;

        if (compressionOffset > offsetTreshhold)
        {
            if (isCompressionTimerRunning == false)
            {
                isCompressionTimerRunning = true;
                compressionTimer.Start();
            }

            isCompressedEnough = true;
        }
        else if (compressionOffset == 0 && isReadyToRefresh == true)
        {
            InvokeRefresh();
        }
        else
        {
            isCompressedEnough = false;
            isCompressionTimerRunning = false;
        }
    }
}

The second timer brings the control to ‘release-to-refresh’ mode:

private void CompressionTimer_Tick(object sender, object e)
{
    if (isCompressedEnough)
    {
        VisualStateManager.GoToState(this, VisualStateReadyToRefresh, true);
        isReadyToRefresh = true;
    }
    else
    {
        isCompressedEnough = false;
        compressionTimer.Stop();
    }
}

The VisualStateManager in the control’s template deals with the visual transitions - the visibility of the messages, and the rotation of the arrow:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="VisualStateGroup">
        <VisualStateGroup.Transitions>
            <VisualTransition GeneratedDuration="0:0:0.2"
                                To="ReadyToRefresh" />
        </VisualStateGroup.Transitions>
        <VisualState x:Name="Normal" />
        <VisualState x:Name="ReadyToRefresh">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                Storyboard.TargetName="TextPull">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Collapsed</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                Storyboard.TargetName="TextRefresh">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
                <DoubleAnimation Duration="0"
                                    To="0.5"
                                    Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
                                    Storyboard.TargetName="Arrow"
                                    d:IsOptimized="True" />
            </Storyboard>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

The PullToRefreshListView publishes its request for a refresh by raising an event –to notify its view- and executing a command – to notify its MVVM viewmodel:

private void InvokeRefresh()
{
    isReadyToRefresh = false;
    VisualStateManager.GoToState(this, VisualStateNormal, true);

    if (RefreshContent != null)
    {
        RefreshContent(this, EventArgs.Empty);
    }

    if (RefreshCommand != null && RefreshCommand.CanExecute(null) == true)
    {
        RefreshCommand.Execute(null);
    }
}

The control not only works on the phone, but also on Windows 8.1. It works as expected on tablets and touch screens: pull the ListView down, wait half a second to release, and its request for refresh is published. Unfortunately this only works in touch mode: with a mouse it’s impossible to pull down the ListView. So to make the PullToRefreshListView a little more universal, I added the following code: when no touch screen is detected (by checking TouchCapabilities.TouchPresent), then the control displays a refresh button in the top right corner:

RefreshButton

Here’s the corresponding code:

private void PullToRefreshScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
    // Show Refresh Button on non-touch device.
    if (new Windows.Devices.Input.TouchCapabilities().TouchPresent == 0)
    {
        var refreshButton = (Button)GetTemplateChild(RefreshButton);
        refreshButton.Visibility = Visibility.Visible;
        refreshButton.Click += RefreshButton_Click;
    }

    // Timers setup
    // ...
}

private void RefreshButton_Click(object sender, object e)
{
    InvokeRefresh();
}

The control and its sample app live here on GitHub. The solution was created with Visual Studio 2015 RC and is compatible with Visual Studio 2013.

Enjoy!

XAMLBrewer

Capability-driven App Bar for List Items

Lots of Windows 8.1 Store Apps come with a page that shows a list of items that have commands attached to them (like edit, copy, and delete). This article describes how to bind the command bar experience of this type of screen to the capabilities of the device. I created a Windows 8.1 XAML MVVM-style sample app that detects the presence of a mouse and/or a touch screen, and then decides to display the commands that are specific to an item in one of three ways:

  • inside the item that has the mouse, or
  • inside the item that is selected, or
  • in the main bottom app bar.

Here’s how the app looks like. For testing purposes, it allows to change the input mode at runtime:

CommandBar_Desktop

According to the Store App user interface guidance, we should always display such action commands in the bottom app bar (and navigation commands at the top). That what I always did in my apps … well … until users started to complain. The “Touch First” principle has always been one of the main drivers of the Store app UI experience. In reality, the vast majority of PC’s that run these apps still has NO touch screen, and the mouse is the main input device. Here’s the standard workflow for editing a listview item using the mouse in a touch-first environment:

  1. Select the item,
  2. assume the presence of a bottom app bar with related commands (not obvious for users that don’t use the Modern UI on a daily basis),
  3. open the command bar (e.g. by right clicking),
  4. click the corresponding app bar button (which on a large screen can be VERY far away from the item you selected), and finally
  5. explicitly close the app bar again (e.g. by clicking somewhere on the page). Clicking another item doesn’t select that item, but it closes the app bar. Confusing.

Here’s that same scenario in Microsoft’s standard Mail app:

  1. Hover with the mouse over an item, and
  2. click on the inline button. Done.

I think it’s clear that non-touch users would prefer the latter scenario. Here’s how this looks like in the Mail app:

Outlook_1

In touch mode, the inline buttons don’t appear, instead they appear in the ‘classic’ bottom app bar:

Outlook_2

The presence of that app bar is indicated by a hint at the bottom of the screen (it’s actually more than a hint: if you click or tap on the ellipsis, the app bar opens):

Outlook_3 

This is one of the UI elements that has become a standard in my own apps. If you want one in your own app, just install the AppBarHint control by Dave Smits. Here’s how to use that control in XAML - just place it at the bottom. The control will lookup the app bar, and link to it:

<Grid>
    <!— Main UI ... -->

    <!-- AppBar Hint -->
    <local:CollapsedAppBar VerticalAlignment="Bottom"
                           Grid.RowSpan="10"
                           Grid.ColumnSpan="10" />
</Grid>

The Mail app experience inspired me to create some components to enhance the user experience –based on the device’s capabilities- for Windows 8.1 Store app pages that are built around a list of items.

Device Capabilities

Let’s start with an enumeration for the input modes:

public enum InputMode
{
    Desktop,
    TouchEnabled,
    TouchOptimized
}

Basically Desktop means ‘no touch screen detected’, TouchOptimized means ‘no mouse detected’, and TouchEnabled covers all other scenarios. The Device class exposes the input mode – in an MVVM architecture it would be part of a service. The initial input mode is detected automatically, based on calls to members in the Windows.Devices.Input namespace. It can be overridden by the user at any time, that’s why it comes as a Singleton with property change notification:

class Device : BindableBase
{
    private readonly static Device _instance;
    private InputMode _inputMode;

    /// <summary>
    /// Represents the device and its capabilities.
    /// </summary>
    static Device()
    {
        _instance = new Device();

        // No touch -> Desktop
        if (new Windows.Devices.Input.TouchCapabilities().TouchPresent == 0)
        {
            _instance._inputMode = InputMode.Desktop;
            return;
        }

        // No mouse -> Touch Optimized
        if (new Windows.Devices.Input.MouseCapabilities().MousePresent == 0)
        {
            _instance._inputMode = InputMode.TouchOptimized;
            return;
        }

        // Otherwise -> Touch Enabled
        _instance._inputMode = InputMode.TouchEnabled;
    }

    public static Device Instance
    {
        get { return _instance; }
    }

    public InputMode InputMode
    {
        get { return this._inputMode; }
        set { this.SetProperty(ref this._inputMode, value); }
    }
}

To make the input mode available to all view models in the app, we place it in the ViewModelBase base class:

class ViewModelBase : BindableBase
{
    public InputMode InputMode
    {
        get { return Device.Instance.InputMode; }
        set
        {
            Device.Instance.InputMode = value;
            this.OnPropertyChanged("InputMode");
        }
    }
}

Let’s start with the implementation of the different behaviors.

Desktop Mode

First comes Desktop mode, where the item specific commands are represented by app bar buttons in the item template. The buttons appear on the item on which the mouse hovers – without the need for actually selecting the item itself:

CommandBar_Desktop

To deal with the hovering aspect, we introduce a HasPointer property. It’s in a base class for all viewmodels that represent an item. The property will only become true when the item has the mouse focus, but only in Desktop mode. That’s the only mode in which we are sure that the PointerEntered and PointerExited events that trigger the value change, are generated by the mouse. In touch or pen mode the exact same events are triggered, and unfortunately there’s no way to detect the source of the events: touch, pen, mouse, or other. In touch mode, the HasPointer property would create a very confusing behavior: when the user slides into an item the command buttons would appear. But when he lifts his finger to tap one of the buttons, the PointerExited event would fire and they would disappear. The same happens when an item is selected by touch or pen: both PointerEntered and PointerExited events would fire (well: PointerEntered only when the device detects a move), and the command bar will just briefly flash.

Here’s ItemViewModelBase class, hosting the HasPointer implementation:

/// <summary>
/// Base class for viewmodels that represent an item in a list.
/// </summary>
class ItemViewModelBase : ViewModelBase
{
    private bool hasPointer = false;

    // ... (other stuff)

    public bool HasPointer
    {
        get { return this.hasPointer; }
        set
        {
            // Property is only active in Desktop mode.
            if (this.InputMode == Services.InputMode.Desktop)
            {
                this.SetProperty(ref this.hasPointer, value);
            }
        }
    }
}

Here’s the XAML definition of the inline command bar. It’s using standard app bar buttons in a stackpanel of which the visibility is bound to the HasPointer property:

<ListView.ItemTemplate>
    <DataTemplate>
        <!-- ... (more stuff) -->
            <StackPanel Visibility="{Binding HasPointer, Converter={StaticResource BooleanToVisibilityConverter}}">
                <AppBarButton Label="Smell"
                                Command="{Binding SmellCommand}"
                                RequestedTheme="Dark">
                </AppBarButton>
                <AppBarButton Label="Eat"
                                Command="{Binding EatComand}"
                                RequestedTheme="Dark">
                </AppBarButton>
            </StackPanel>
        <!-- ... (more stuff) -->
    </DataTemplate>
</ListView.ItemTemplate>

The HasPointer property is changed by the view through a behavior:

<ListView.ItemTemplate>
    <DataTemplate>
        <Grid>
        <!-- ... (again more stuff) -->
            <i:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="PointerEntered">
                    <core:ChangePropertyAction PropertyName="HasPointer"
                                                Value="True"
                                                TargetObject="{Binding}" />
                </core:EventTriggerBehavior>
                <core:EventTriggerBehavior EventName="PointerExited">
                    <core:ChangePropertyAction PropertyName="HasPointer"
                                                Value="False"
                                                TargetObject="{Binding}" />
                </core:EventTriggerBehavior>
            </i:Interaction.Behaviors>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

For Visual Studio 2013, this implies that you need to install the Behaviors SDK through the Extensions menu, and add a reference to it:

Behaviors_SDK_1

Visual Studio 2015 RC does not require a separate install: the SDK comes with the Visual Studio/Blend package.

For the sake of completeness: Desktop mode can probably also be implemented using a PointerOver visual state.

TouchEnabled Mode

The second input mode is TouchEnabled. In this mode, the item specific commands still appear still appear inside the item template, but only in the selected item:

CommandBar_TouchEnabled

The notion of having an item selected (HasSelection) and the SelectedItem itself, are implemented ListViewModelBase<T>, a generic base class for the viewmodels that represent a list. When the selection changes, the previous item is unselected (if there was one), the new item is selected (if there is one), and all bindings to HasSelection are updated:

class ListViewModelBase<T> : ViewModelBase where T : ItemViewModelBase
{
    private T selectedItem;

    public T SelectedItem
    {
        get { return this.selectedItem; }
        set
        {
            if (this.selectedItem != null)
            {
                this.selectedItem.IsSelected = false;
            }

            this.SetProperty(ref this.selectedItem, value);

            if (this.selectedItem != null)
            {
                this.selectedItem.IsSelected = true;
            }

            this.OnPropertyChanged("HasSelection");
        }
    }

    public bool HasSelection
    {
        get { return this.selectedItem != null; }
    }
}

In the view, the selected item of the listview is two-way bound to the one in the viewmodel:

<ListView SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <!-- (all the rest) -->
</ListView>

The item itself comes with an IsSelected property, again in the ItemViewModelBase class. When an item is selected in TouchEnabled mode, it updates the HasPointer property, and the inline commands become visible:

public bool IsSelected
{
    get { return this.isSelected; }
    set
    {
        this.SetProperty(ref this.isSelected, value);
        // Update HasPointer if in TouchEnabled mode.
        if (this.InputMode == Services.InputMode.TouchEnabled)
        {
            this.hasPointer = value;
            this.OnPropertyChanged("HasPointer");
        }
    }
}

TouchOptimized Mode

Let’s move on to the third input mode: TouchOptimized is chosen when no mouse is detected. In this mode we stick to the prescribed UI: an app bar at the bottom of the page:

CommandBar_TouchOptimized

The item specific commands are part of the standard bottom app bar, but only appear in TouchEnabled mode, and only when there’s an item selected. For the implementation we cannot use a PrimaryCommands or SecondaryCommands element here, because these elements don’t have a Visibility property. So we have to help ourselves with some good old stack panels:

<StackPanel Orientation="Horizontal"
            Visibility="{Binding InputMode, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter='TouchOptimized'}">
    <StackPanel Orientation="Horizontal"
                Visibility="{Binding HasSelection, Converter={StaticResource BooleanToVisibilityConverter}}">
        <!-- AppBarButtons ... --> 
    </StackPanel>
</StackPanel>

When the odds are high that the user is going to execute a command after he selected an item, then it makes sense to automatically open the command bar for him. That’s something that the view can handle:

<ListView SelectionChanged="CheesePlate_SelectionChanged">
  <!-- ... -->
</ListView>

When an item is selected we open the bottom app bar, and make it sticky. When the user taps on another item, the app bar remains open. In the standard (non-sticky) mode, when the user taps another item that item is not selected, but the tap event is wasted to close the app bar. I can assure you: most users don’t like that! Alternatively you could close the app bar using a timer. Anyway, here’s the event handler that opens and closes the bottom app bar:

private void CheesePlate_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (Device.Instance.InputMode == InputMode.TouchOptimized && e.AddedItems.Count == 1)
    {
        this.BottomAppBar.IsOpen = true;
        this.BottomAppBar.IsSticky = true;
    }
    else
    {
        this.BottomAppBar.IsOpen = false;
    }
}

By the way, the Mail app uses yet another approach. When you tap a message, it is highlighted, but the command bar remains closed:

Mail_Touch_1

When you tap the check box in the item template, the command bar pops open:

Mail_Touch_2

As you see, there are a lot of options available to make the manipulation of a list of items more user friendly, based on the capabilities of the device. I focused on techniques that can be implemented easily in existing Windows 8.1 Store apps: all code is in the views and in high level framework classes.

The sample app is available on GitHub. It was built using Visual Studio 2013 Update 4 and Visual Studio 2015 RC (yes: the solution and project files are interchangeable).

Enjoy,

XAML Brewer

Using the OneDrive SDK in universal apps

This article shows how to use the new OneDrive SDK in a Windows universal app. It presents a sample app with the following features:

  • Singing in with a Windows account.
  • Creating a working folder. 
  • Browsing the content of a working folder.
  • Downloading a file.
  • Uploading a file.
  • Deleting a file.
  • Fetching the changes in a folder since the last check.

Here’s how that app looks like on the PC (it also runs on the phone):

screenshot_04042015_084243

Some weeks ago, Microsoft launched a new OneDrive API with a couple of new features, like easy synchronization and support for very large files. The API also came with three sample projects, one in HTML/Javascript,, one in WinForms/C#, and one for Android (yup: no XAML). The Windows Forms sample contains the source of the new OneDrive SDK. And I quote: ‘An example library for interacting with OneDrive. This library is implemented as a Portable Class Library and can be used on a number of target platforms including .NET Framework 4.5, Windows 8, Windows Phone 8.1, and Windows Phone Silverlight 8’. The SDK is a lightweight wrapper around the OneDrive OData REST API.

Here are some of the key classes in this new SDK:

  • The ODItem class represents a file or a folder on your OneDrive. It contains all their properties, such as path, status, size, and thumbnail.
  • The ODConnection class represents your connection to your OneDrive. It contains the CRUD methods to work with files and folders.
  • The IAuthenticationInfo interface describes the authentication requirements. The SDK does not contain an implementation since that depends on the technology you’re using.

 SDK_API

Authenticating to Live

You have to provide an authentication infrastructure to use the SDK. It expects a class that gets a token for your Windows Account, and that implements the SDK’s IAuthenticationInfo interface. Of course you could rely on the Live SDK to authenticate (instructions can be found here), but the OneDrive SDK is actually a replacement for this Live SDK. So it makes more sense to create (or find) a lightweight class on top of the Live OAuth services. I did not built class from scratch: fortunately I could reuse the authentication and authorization calls from another sample, the OneNote API universal app sample. After all, both OneNote and OneDrive are both Live services that sit behind the same authentication infrastructure.

The authentication is done through an OnlineIdAuthenticator instance.

Here’s the beef of the code:

// TODO: Replace the below scopes with the least permissions your app needs
private const string Scopes = "wl.signin onedrive.readwrite";

// TODO: Replace the below ClientId with your app's ClientId.
private const string ClientId = "<YOUR APP's CLIENT ID HERE>";

private const string MsaTokenRefreshUrl = "https://login.live.com/oauth20_token.srf";
private const string TokenRefreshContentType = "application/x-www-form-urlencoded";
private const string TokenRefreshRedirectUri = "https://login.live.com/oauth20_desktop.srf";

private const string TokenRefreshRequestBody = "client_id={0}&redirect_uri={1}&grant_type=refresh_token&refresh_token={2}";

/// <summary>
/// Gets a valid authentication token. Also refreshes the access token if it has expired.
/// </summary>
/// <remarks>
/// Used by the API request generators before making calls to the OneNote and OneDrive APIs.
/// </remarks>
/// <returns>valid authentication token</returns>
internal static async Task<string> GetAuthToken()
{
    if (String.IsNullOrWhiteSpace(_accessToken))
    {
        try
        {
            var serviceTicketRequest = new OnlineIdServiceTicketRequest(Scopes, "DELEGATION");
            var result =
                await Authenticator.AuthenticateUserAsync(new[] { serviceTicketRequest }, CredentialPromptType.PromptIfNeeded);
            // TODO: catch exception when user says no
            if (result.Tickets[0] != null)
            {
                _accessToken = result.Tickets[0].Value;
                _accessTokenExpiration = DateTimeOffset.UtcNow.AddMinutes(AccessTokenApproxExpiresInMinutes);
            }
        }
        catch (Exception ex)
        {
            // Authentication failed
            if (Debugger.IsAttached) Debugger.Break();
        }
    }
    await RefreshAuthTokenIfNeeded();
    return _accessToken;
}

/// <summary>
///     This method tries to refresh the access token. The token needs to be
///     refreshed continuously, so that the user is not prompted to sign in again
/// </summary>
/// <returns></returns>
private static async Task AttemptAccessTokenRefresh()
{
    var createMessage = new HttpRequestMessage(HttpMethod.Post, MsaTokenRefreshUrl)
    {
        Content = new StringContent(
            String.Format(CultureInfo.InvariantCulture, TokenRefreshRequestBody,
                ClientId,
                TokenRefreshRedirectUri,
                _refreshToken),
            Encoding.UTF8,
            TokenRefreshContentType)
    };

    var httpClient = new HttpClient();
    HttpResponseMessage response = await httpClient.SendAsync(createMessage);
    await ParseRefreshTokenResponse(response);
}

In a universal app, the very first time that GetAuthToken is called, the user has to give consent, based on the Scopes in the code (wl.signin and onedrive.readwrite):

Consent

This only works if your universal app is registered in the Store(s). Otherwise you don’t have a Client ID.

Here’s how to find the Client ID of your app. Register your app in the Store and associate is with your project. Then go to the app’s Services configuration in the Store dashboard. From there you can follow the link to the Live Services site that reveals the Client ID:

app_services

app_live

Connecting to your OneDrive

Once authenticated, all you need to do is present your token to the OneDrive SDK through the IAuthenticationInfo interface to create an ODConnection that gives you access to a all the API calls. To protect my viewmodels from the internals of the SDK, I created my own MyOneDrive class in between. Here’s its Login method:

/// <summary>
/// Login and create the connection, if necessary.
/// </summary>
public static async Task Login()
{
    if (Connection == null || !LiveOAuth.IsSignedIn)
    {
        // Get an OAuth2 access token through REST.           
        var token = await LiveOAuth.GetAuthToken();

        // Initialize connection
        Connection = new ODConnection("https://api.onedrive.com/v1.0", new OneDriveSdkAuthenticationInfo(token));
    }
}

The rest of the methods in that class will throw an error if the user is not authenticated. They all start with a call to EnsureConnection:

private static void EnsureConnection()
{
    if (Connection == null || !LiveOAuth.IsSignedIn)
    {
        throw new Exception("You're not logged in.");
    }
}

Browsing OneDrive content

For walking the OneDrive tree, you start with getting a reference to the root. And that’s just what GetRootItemAsync does:

// Connect to OneDrive root.
var rootFolder = await Connection.GetRootItemAsync(ItemRetrievalOptions.DefaultWithChildren);

From there you can navigate on the Connection, and enumerate or search for folders and files using methods such as GetChildrenOfItemAsync and SearchForItemsAsync. Some of these calls are also available on ODItem as extension methods. For the REST (pun intended) that ODItem class is just a JSON data transfer object. Here’s how the MyOneDrive class fetches the files in a specific folder. Since folders can be large –look at your pictures/camera roll folder- the API provides paging. So you have to iterate:

public static async Task<IEnumerable<ODItem>> GetFiles(ODItem parentItem)
{
    EnsureConnection();

    List<ODItem> result = new List<ODItem>();
    var page = await parentItem.PagedChildrenCollectionAsync(Connection, ChildrenRetrievalOptions.Default);

    var list = from f in page.Collection
                where f.File != null
                select f;

    result.AddRange(list);

    while (page.MoreItemsAvailable())
    {
        list = from f in page.Collection
                where f.File != null
                select f;

        result.AddRange(list);

        page = await page.GetNextPage(Connection);
    }

    return result;
}

To download the contents of a file, just call GetContentsStreamAsync:

public static async Task<Stream> DownloadFile(ODItem item)
{
    EnsureConnection();

    return await item.GetContentStreamAsync(Connection, StreamDownloadOptions.Default);
}

Modifying files and folders

Typically an app that uses OneDrive services would have a working folder that contains all its files. That concept is supported by the OneDrive API through the App Folder: Apps/YourAppName is the pattern. This concept does not exist in the SDK (yet).

The app’s working folder needs to be created. Here’s the method that I call after signing in. It looks for a folder with a specific name (directly under the root folder), and creates it if necessary:

public static async Task<ODItem> SetWorkingFolder(string folderName)
{
    EnsureConnection();

    // Connect to OneDrive root.
    var rootFolder = await Connection.GetRootItemAsync(ItemRetrievalOptions.DefaultWithChildren);

    // Search working folder
    var existingItem = (from f in rootFolder.Children
                        where f.Name == folderName
                        select f).FirstOrDefault();

    if (existingItem != null)
    {
        if (existingItem.Folder == null)
        {
            // There is already a file with this name.
            throw new Exception("There is already a file with this name.");
        }
        else
        {
            // The folder already exists.
            WorkingFolder = existingItem;
            return WorkingFolder;
        }
    }

    var newFolder = await Connection.CreateFolderAsync(rootFolder.ItemReference(), folderName);
    WorkingFolder = newFolder;
    return WorkingFolder;
}

To upload a new or a modified file, just call PutNewFileToParentItemAsync:

public static async Task<ODItem> SaveFile(ODItem parentItem, string filename, Stream fileContent)
{
    EnsureConnection();

    return await Connection.PutNewFileToParentItemAsync(parentItem.ItemReference(), filename, fileContent, ItemUploadOptions.Default);
}

Of course, there’s also a DeleteItemAsync:

public static async Task<bool> DeleteItem(ODItem item)
{
    EnsureConnection();

    return await Connection.DeleteItemAsync(item.ItemReference(), ItemDeleteOptions.Default);
}

As you see, CRUD operations on OneDrive files and folders are easy.

Enumerating changes

A fantastic new feature in the OneDrive API is the ability to check for changes in a folder in an incremental way. The ViewChangesAsync method returns not only the changes to a folder, but also a token that the app can store (e.g. in its settings). That token can then be presented in the next call to ViewChangesAsync to get the delta. The ODViewChangesResult contains details on the items that were added, modified, or deleted since the token was generated.

There are some apps out there that use a OneDrive folder to synchronize their working data across devices. With the ‘old’ Live SDK that is a hard and error prone job (sample code here), with the new OneDrive API this becomes a piece of cake.

Here’s the ViewChanges from the MyOneDrive class. It stores the token in a variable. It also allows to reset the token, in which case a full list of children from the folder is returned. I’ll explain this in a minute:

private static string Token = string.Empty;

/// <summary>
/// Returns the list of changes to the working folder since the previous call.
/// </summary>
/// <param name="reset">Whether or not to reset the token.</param>
/// <remarks>In the first call and whenever the token is reset, the whole folder's content is returned.</remarks>
public static async Task<ODViewChangesResult> ViewChanges(bool reset = false)
{
    EnsureConnection();

    if (reset)
    {
        Token = null;
    }

    var options = string.IsNullOrEmpty(Token) ? ViewChangesOptions.Default : new ViewChangesOptions() { StartingToken = Token };

    var result = await Connection.ViewChangesAsync(WorkingFolder.ItemReference(), options);
    Token = result.NextToken;

    return result;
}

In a real-world app, I assume that paging will also be required here. The default is 200 changes per page.

Wrap-up

Here are the classes that I created to use the new OneDrive SDK in a universal app:

  • LiveOAuth takes care of the authentication. It’s largely copied from the sample OneNote authenticator.
  • OneDriveSdkAuthenticationInfo makes the previous class compatible with the OneDrive SDK by implementing the IAuthentication interface.
  • MyOneDrive is an MVVM service that provides a friendly facade to the SDK.

This is the corresponding class diagram:

My_API

The full documentation of the new OneDrive SDK is coming soon, I also assume that Microsoft will publish it as a Nuget package.

I’m already a fan of this SDK: it’s more lightweight than the full Live SDK (which requires a local install on the development machine) and it comes with very useful new features. It’s also good to see the full source on GitHub, since some of our customers don’t like black box dll’s in their projects.

A warning for Windows Phone developers

Due to asynchronicity and caching there may be some delays in getting the exact current state of your OneDrive content. The following animation shows the sample app running on a phone. You’ll notice the following:

  • User tries to browse content without signing in, and gets an error.
  • User signs in.
  • User fetches content of the working folder. 8 sample files are returned.
  • User selects file 1, and successfully deletes it.
  • User fetches changes. The token was empty so the full content of the working folder is returned. File 1 is not in this list.
  • User selects file 2, and successfully deletes it.
  • User fetches changes. The deletion of file 2 is returned.
  • User browses content of working folder. File 1 and File 2 are still returned.
  • User fetches changes with an empty token. File 2 is still in this list.

 Phone_Sample

I was not able to reproduce this delay on the PC. There the information was always up-to-date, after all a PC is continuously synchronizing that drive.

So you may not assume that the information is real-time. But don’t let that stop you: it’s probably real-enough time.

Source code

The full source code of the sample includes a non-maintained version of the source code of the OneDrive SDK, and lives on GitHub. It was written in Visual Studio 2013. [The odds are high that this will be my last blog project in that version.]

Enjoy!

XAML Brewer

An animated ScrollToSection for the Universal Hub Control

n this article I show a way to animate the scrolling to a particular HubSection of a Hub in a Universal XAML Windows app. The native Hub.ScrollToSection method comes without such an animation: instead of smoothly scrolling, it jumps directly to the target section. That’s quite scary. A lot of developers seem to encounter this problem, but I didn’t find a working solution anywhere. So here are my 2 cents.

The sample app represents the type of Windows Phone application that I created this animation for. The app’s main structure is a Hub. The first HubSection in this hub acts a menu: it contains a list of hyperlinks to the other sections in the hub. The rest of the navigation buttons are just there to verify the algorithm: it should be possible to scroll from whatever section to whatever other section, backward as well as forward:

wp_ss_20150223_0001 wp_ss_20150223_0008 wp_ss_20150223_0005 wp_ss_20150223_0006

 

The Hub itself does not have a HorizontalOffset or so. The property we need to animate here is found in its internal structure: the HorizontalOffset of the embedded ScrollViewer control. Unfortunately this is a read-only property, we can not change it through a DoubleAnimation in a StoryBoard. The scroll position can only be changed by calling one of the ChangeView method overloads. Oddly enough, if you want animation, you have to call the ChangeView that allows you to disable … animation. Here’s the official description of the disableAnimation parameter: “True to disable zoom/pan animations while changing the view; otherwise, false. The default is false”. Well, that default value ruins scrolling on the phone. The default animation is visually empty, but it probably takes some time and during that time it absorbs your own animations. So you have to explicitly disable the system animation by setting the parameter to True.

The confusion doesn’t stop here. This is the official description of the horizontalOffset parameter, the first in the ChangeView call: “A value between 0 and ScrollableWidth that specifies the distance the content should be scrolled horizontally.” To me that  sounds like the method expects a delta; e.g. +10 if you want to scroll 10 pixels to the right. It turns out that this horizontalOffset is actually the target position: with +10 you navigate to the 10th pixel in the Hub.

So here’s a working implementation of smoothly scrolling to a specific section in a hub. We first calculate the absolute current position and the relative position of the target section. Then we start to scroll towards the target in a number of steps, with an increasing wait time between the steps to simulate an easing function. That sounds cheap, but it does the trick:

public async static Task ScrollToSectionAnimated(this Hub hub, HubSection section)
{
    // Find the internal scrollviewer and its current horizontal offset.
    var viewer = hub.GetFirstDescendantOfType<ScrollViewer>();
    var current = viewer.HorizontalOffset;

    // Find the distance to scroll.
    var visual = section.TransformToVisual(hub);
    var point = visual.TransformPoint(new Point(0, 0));
    var offset = point.X;

    // Scroll in a more or less animated way.
    var increment = offset / 24;
    for (int i = 1; i < 25; i++)
    {
        viewer.ChangeView((i * increment) + current, null, null, true);
        await Task.Delay(TimeSpan.FromMilliseconds(i * 20));
    }
}

The ScrollToSectionAnimated method is implemented as an extension method. I added some extra methods in the surrounding class, to fetch the current section of the hub, and the index of this section. These make particularly sense in a Windows Phone app, where there is only one section visible. That’s the first one in Hub.SectionsInView:

public static HubSection CurrentSection(this Hub hub)
{
    if (hub.Sections.Count > 0)
    {
        return hub.SectionsInView[0];
    }
    else
    {
        return null;
    }
}

public static int CurrentIndex(this Hub hub)
{
    if (hub.Sections.Count > 0)
    {
        return hub.Sections.IndexOf(hub.CurrentSection());
    }
    else
    {
        return -1;
    }
}

So here’s the code to scroll to the next and the last section, starting from the current position in the hub:

private async void ScrollToNext_Clicked(object sender, RoutedEventArgs e)
{
    // Find out where we are.
    var index = this.MainHub.CurrentIndex();

    await this.MainHub.ScrollToSectionAnimated(this.MainHub.Sections[index + 1]);
}

private async void ScrollToLast_Clicked(object sender, RoutedEventArgs e)
{
    await this.MainHub.ScrollToSectionAnimated(this.MainHub.Sections.Last());
}

Here's how the scrolling looks like in action: