Windows 8 XAML Tips - Settings Demo

By Fons Sonnemans, 10-8-2012

In my previous blog post I explained how you can use the PaneThemeTransition from the Animation Library to show a task pane. In this blog post I want to use this technique to open my Settings and About task panes from the Settings Charm. I will try to make it easier by introducing a TaskPanePopup helper class.

TaskPanePopup

You construct an instance of the TaskPanePopup class using a task pane, in most cases this will be a user control. In the constructor a Popup control is created and initialized with the Child (the task pane) and the ChildTransitions (PaneThemeTransition coming from the right). The Width of the task pane must be set to calculate the position of the Popup. The task pane will be shown in a Popup using the animation when you call the Show() method on the instance.

public class TaskPanePopup {

    private Popup _popup;
    public FrameworkElement TaskPane { get; private set; }

    public TaskPanePopup(FrameworkElement taskPane) {
        if (double.IsNaN(taskPane.Width)) {
            throw new ArgumentException("TaskPane width must be set");
        }
        this.TaskPane = taskPane;

        this._popup = new Popup {
            IsLightDismissEnabled = true,
            ChildTransitions = new TransitionCollection(),
            Child = taskPane,
        };

        this._popup.ChildTransitions.Add(new PaneThemeTransition {
            Edge = EdgeTransitionLocation.Right
        });
    }

    public void Show() {
        this.TaskPane.Height = Window.Current.Bounds.Height;
        this._popup.SetValue(
                Canvas.LeftProperty,
                Window.Current.Bounds.Width - this.TaskPane.Width
        );
        this._popup.IsOpen = true;
    }

}

Settings Charm

To demonstrate the use of my class I have created a sample appliation. This app must have a Settings and About command in the Settings Charm. Therefore I have subscribed the app on the CommandsRequested event of the current SettingsPane (line 30). In the event handler I have created two SettingsCommand objects and added them to the ApplicationCommands. These Commands create the TaskPanePopup objects in their handlers and shows them.

sealed partial class App : Application
{
    /// <summary>
    /// Initializes the singleton Application object.  This is the first line of authored code
    /// executed, and as such is the logical equivalent of main() or WinMain().
    /// </summary>
    public App()
    {
        this.InitializeComponent();
        this.Suspending += OnSuspending;
    }

    /// <summary>
    /// Invoked when the application is launched normally by the end user.  Other entry points
    /// will be used when the application is launched to open a specific file, to display
    /// search results, and so forth.
    /// </summary>
    /// <param name="args">Details about the launch request and process.</param>
    protected override async void OnLaunched(LaunchActivatedEventArgs args)
    {
        // Do not repeat app initialization when already running, just ensure that
        // the window is active
        if (args.PreviousExecutionState == ApplicationExecutionState.Running)
        {
            Window.Current.Activate();
            return;
        }

        // Init SettingsPane
        SettingsPane.GetForCurrentView().CommandsRequested += App_CommandsRequested;

        // Create a Frame to act as the navigation context and associate it with
        // a SuspensionManager key
        var rootFrame = new Frame();
        SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            // Restore the saved session state only when appropriate
            await SuspensionManager.RestoreAsync();
        }

        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter
            if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
            {
                throw new Exception("Failed to create initial page");
            }
        }

        // Place the frame in the current Window and ensure that it is active
        Window.Current.Content = rootFrame;
        Window.Current.Activate();
    }

    private TaskPanePopup _settings;
    private TaskPanePopup _about;

    private void App_CommandsRequested(SettingsPane sender,
                                       SettingsPaneCommandsRequestedEventArgs args) {
            
        SettingsCommand cmd = new SettingsCommand("Settings", "Settings", (command) => {
            if (_settings == null) {
                _settings = new TaskPanePopup(new SettingsControl());
            }
            _settings.Show();
        });

        args.Request.ApplicationCommands.Add(cmd);

        cmd = new SettingsCommand("About", "About", (command) => {
            if (_about == null) {
                _about = new TaskPanePopup(new AboutControl());
            }
            _about.Show();
        });

        args.Request.ApplicationCommands.Add(cmd);
    }

If you start the application and open the Settings Charm (WinKey-I) you will get the following Settings Charm.

If you select the Settings command the SettingsControl will popup using the correct animation.

Task Pane User Control

A task pane is in my case a User Control with a back button, title and content. This content is shown using an EntranceThemeTransition from the Animation Library. This will bring this app to life.

XAML of my SettingsControl user control

<UserControl
    x:Class="SettingsDemo.SettingsControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SettingsDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="768"
    Width="350">

    <Grid
        Background="Crimson">
        <Grid.ColumnDefinitions>
            <ColumnDefinition
                Width="45" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition
                Height="100" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid
            VerticalAlignment="Bottom"
            Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition
                    Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Button
                Click="ButtonBack_Click"
                HorizontalAlignment="Stretch"
                Margin="0"
                Style="{StaticResource SnappedBackButtonStyle}"
                VerticalAlignment="Bottom" />
            <TextBlock
                Text="Settings"
                Grid.Column="1"
                VerticalAlignment="Top"
                Style="{StaticResource SubheaderTextStyle}"
                FontFamily="Segoe UI Semibold" />
        </Grid>
        <StackPanel
            Grid.Row="1"
            Grid.Column="1">
            <StackPanel.ChildrenTransitions>
                <TransitionCollection>
                    <EntranceThemeTransition
                        FromHorizontalOffset="100" />
                </TransitionCollection>
            </StackPanel.ChildrenTransitions>
            <TextBlock
                TextWrapping="Wrap"
                Text="Place your content here"
                Style="{StaticResource BasicTextStyle}" />
        </StackPanel>
    </Grid>
</UserControl>

C# code behind of my SettingsControl user control

public sealed partial class SettingsControl : UserControl {

    public SettingsControl() {
        this.InitializeComponent();
    }

    private void ButtonBack_Click(object sender, RoutedEventArgs e) {
        var popup = this.Parent as Popup;
        if (popup != null) popup.IsOpen = false;
        SettingsPane.Show(); 
    } 
}

Closure

I hope you like my solution. You can download my code below.

Cheers,

Fons

Leave a Comment

Leave a Comment
Name
Comment
5 + 8 =

0 Comments

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.