1

I would like to create an app bar like the one in the image below with XAML if possible, can this be achieved? In I'm also wondering if it is possible for an app bar to open when the user taps or clicks the 3 dots:

app bar

XAML

<Page
    x:Name="pageRoot"
    x:Class="Exits_Expert_London_Lite.Lines_and_Stations.WC.Bank__WC_"
    DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Exits_Expert_London_Lite.Lines_and_Stations.WC"
    xmlns:common="using:Exits_Expert_London_Lite.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:q42controls="using:Q42.WinRT.Controls"
    mc:Ignorable="d">

    <Page.Resources>
        <x:String x:Key="ChevronGlyph">&#xE26B;</x:String>
    </Page.Resources>

    <!--
        This grid acts as a root panel for the page.
    -->
    <Grid Background="Black">
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition/>
            </TransitionCollection>
        </Grid.ChildrenTransitions>

        <Grid Name="CustomAppBarRoot" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Loaded="CustomAppBarRoot_OnLoaded"
          ManipulationMode="TranslateY"
          ManipulationDelta="CustomAppBarRoot_OnManipulationDelta"
          ManipulationCompleted="CustomAppBarRoot_OnManipulationCompleted"
          Tapped="CustomAppBarRoot_OnTapped">
            <Grid.RenderTransform>
                <TranslateTransform X="0" Y="0"/>
            </Grid.RenderTransform>

            <Grid.Background>
                <SolidColorBrush Color="Black" Opacity="0.5"></SolidColorBrush>
            </Grid.Background>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock Name="DotsTextBlock" FontSize="28" Text="..." HorizontalAlignment="Right" VerticalAlignment="Top"
                    Margin="0 0 15 0" Tapped="DotsTextBlock_OnTapped" Width="50" Height="50" TextAlignment="Center">
                <TextBlock.RenderTransform>
                    <TranslateTransform Y="0" X="11"/>
                </TextBlock.RenderTransform>
            </TextBlock>

            <StackPanel Name="ButtonsStackPanel" Grid.Row="1" Orientation="Horizontal">
                <AppBarButton Label="tfg" Icon="Add"/>
                <AppBarButton Label="tfg" Icon="Add"/>
            </StackPanel>
        </Grid>

        <Hub>
            <Hub.Header>
                <!-- Back button and page title -->
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="80"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Button  x:Name="backButton" Margin="-1,-1,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                        Style="{StaticResource NavigationBackButtonNormalStyle}"
                        VerticalAlignment="Top"
                        AutomationProperties.Name="Back"
                        AutomationProperties.AutomationId="BackButton"
                        AutomationProperties.ItemType="Navigation Button"/>
                    <TextBlock x:Name="pageTitle" Text="Bank" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                        IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Top" Foreground="#FF66CCCC"/>
                </Grid>
            </Hub.Header>

            <HubSection Width="800" Padding="40,50,0,0">
                <HubSection.Header>
                    <StackPanel>
                        <TextBlock Text="hub section 1" Style="{StaticResource HeaderTextBlockStyle}" Foreground="#FF66CCCC" Margin="0,0,0,5"/>
                    </StackPanel>
                </HubSection.Header>
                <DataTemplate>
                    <Grid>
                        <StackPanel>
                            <TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Margin="0,0,0,5" TextWrapping="Wrap">
                                <Run Text="Hello World" Foreground="#FFFFCC00"/>
                            </TextBlock>

                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </HubSection>
        </Hub>

    </Grid>
</Page>

C# code behind

using Exits_Expert_London_Lite.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;

namespace Exits_Expert_London_Lite.Lines_and_Stations.WC
{
    /// <summary>
    /// A page that displays a grouped collection of items.
    /// </summary>
    public sealed partial class Bank__WC_ : Page
    {
        private NavigationHelper navigationHelper;
        private ObservableDictionary defaultViewModel = new ObservableDictionary();

        /// <summary>
        /// This can be changed to a strongly typed view model.
        /// </summary>
        public ObservableDictionary DefaultViewModel
        {
            get { return this.defaultViewModel; }
        }

        /// <summary>
        /// NavigationHelper is used on each page to aid in navigation and 
        /// process lifetime management
        /// </summary>
        public NavigationHelper NavigationHelper
        {
            get { return this.navigationHelper; }
        }

        public Bank__WC_()
        {
            this.InitializeComponent();
            this.navigationHelper = new NavigationHelper(this);
            this.navigationHelper.LoadState += navigationHelper_LoadState;
            this.Tapped += Page_OnTapped;
        }

        #region custom app bar

        private Storyboard hideCustomAppBarStoryboard;
        private Storyboard showCustomAppBarStoryboard;
        private Size appBarSize;
        private Size appBarButtonsSize;
        private bool isAppBarShown = false;

        private void CustomAppBarRoot_OnLoaded(object sender, RoutedEventArgs e)
        {
            appBarSize = new Size(CustomAppBarRoot.ActualWidth, CustomAppBarRoot.ActualHeight);
            appBarButtonsSize = new Size(ButtonsStackPanel.ActualWidth, ButtonsStackPanel.ActualHeight);
            InitializeStoryboards();

            HideCustomAppBar();
        }

        private void ShowCustomAppBar()
        {
            isAppBarShown = true;
            showCustomAppBarStoryboard.Begin();
        }

        private void HideCustomAppBar()
        {
            isAppBarShown = false;
            hideCustomAppBarStoryboard.Begin();
        }

        private void DotsTextBlock_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            if (isAppBarShown)
                HideCustomAppBar();
            else
                ShowCustomAppBar();
        }

        private void Page_OnTapped(object sender, TappedRoutedEventArgs tappedRoutedEventArgs)
        {
            if (isAppBarShown)
                HideCustomAppBar();
        }

        private void InitializeStoryboards()
        {
            hideCustomAppBarStoryboard = new Storyboard();
            showCustomAppBarStoryboard = new Storyboard();

            var showDoubleAnimation = new DoubleAnimation()
            {
                EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseInOut },
                To = 0,
                Duration = new Duration(TimeSpan.FromMilliseconds(200))
            };
            var hideDoubleAnimation = new DoubleAnimation()
            {
                EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut },
                To = appBarButtonsSize.Height,
                Duration = new Duration(TimeSpan.FromMilliseconds(200))
            };
            hideCustomAppBarStoryboard.Children.Add(hideDoubleAnimation);
            showCustomAppBarStoryboard.Children.Add(showDoubleAnimation);

            Storyboard.SetTarget(hideCustomAppBarStoryboard, CustomAppBarRoot);
            Storyboard.SetTarget(showCustomAppBarStoryboard, CustomAppBarRoot);
            Storyboard.SetTargetProperty(showCustomAppBarStoryboard, "(UIElement.RenderTransform).(TranslateTransform.Y)");
            Storyboard.SetTargetProperty(hideCustomAppBarStoryboard, "(UIElement.RenderTransform).(TranslateTransform.Y)");
        }

        #endregion

        private void CustomAppBarRoot_OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            var translateTransform = (CustomAppBarRoot.RenderTransform as TranslateTransform);

            double newY = e.Delta.Translation.Y + translateTransform.Y;
            translateTransform.Y = Math.Max(0, Math.Min(newY, appBarButtonsSize.Height));
        }

        private void CustomAppBarRoot_OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            if (!isAppBarShown)
                ShowCustomAppBar();
            else
                HideCustomAppBar();
        }

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="sender">
        /// The source of the event; typically <see cref="NavigationHelper"/>
        /// </param>
        /// <param name="e">Event data that provides both the navigation parameter passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
        /// a dictionary of state preserved by this page during an earlier
        /// session.  The state will be null the first time a page is visited.</param>
        private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            // TODO: Assign a collection of bindable groups to this.DefaultViewModel["Groups"]
        }

        #region NavigationHelper registration

        /// The methods provided in this section are simply used to allow
        /// NavigationHelper to respond to the page's navigation methods.
        /// 
        /// Page specific logic should be placed in event handlers for the  
        /// <see cref="GridCS.Common.NavigationHelper.LoadState"/>
        /// and <see cref="GridCS.Common.NavigationHelper.SaveState"/>.
        /// The navigation parameter is available in the LoadState method 
        /// in addition to page state preserved during an earlier session.

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            navigationHelper.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            navigationHelper.OnNavigatedFrom(e);
        }

        #endregion
    }
}
wbk727
  • 8,017
  • 12
  • 61
  • 125

1 Answers1

1

There's no way to create such AppBar without custom controls on Windows 8.1. If you want to code it fast (although it's not reusable solution) you can create Grid element (which would act as AppBar) as last child of "root" Grid with VerticalAlingment=Bottom, HorizontalAlingment=Stretch and animate (RenderTransform).(TranslateTransform.Y) (or some PointAnimation) with some UI logic.

EDIT: example (just example, in real application this should be refactored, improved etc. etc.)

XAML:

<Page
x:Class="CustomAppBar.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CustomAppBar"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">


    <Grid Name="CustomAppBarRoot" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Loaded="CustomAppBarRoot_OnLoaded"
          ManipulationMode="TranslateY"
          ManipulationDelta="CustomAppBarRoot_OnManipulationDelta"
          ManipulationCompleted="CustomAppBarRoot_OnManipulationCompleted"
          Tapped="CustomAppBarRoot_OnTapped">
        <Grid.RenderTransform>
            <TranslateTransform X="0" Y="0"/>
        </Grid.RenderTransform>

        <Grid.Background>
            <SolidColorBrush Color="Black" Opacity="0.5"></SolidColorBrush>
        </Grid.Background>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Name="DotsTextBlock" FontSize="28" Text="..." HorizontalAlignment="Right" VerticalAlignment="Top"
                    Margin="0 0 15 0" Tapped="DotsTextBlock_OnTapped" Width="50" Height="50" TextAlignment="Center">
            <TextBlock.RenderTransform>
                <TranslateTransform Y="0" X="11"/>
            </TextBlock.RenderTransform>
        </TextBlock>

        <StackPanel Name="ButtonsStackPanel" Grid.Row="1" Orientation="Horizontal">
            <AppBarButton Label="tfg" Icon="Add"/>
            <AppBarButton Label="tfg" Icon="Add"/>
        </StackPanel>

    </Grid>
</Grid>

Code-behind:

   public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.Tapped += Page_OnTapped;
    }

    private void Page_OnTapped(object sender, TappedRoutedEventArgs tappedRoutedEventArgs)
    {
        if ( isAppBarShown )
            HideCustomAppBar();
    }

    #region custom app bar

    private Storyboard hideCustomAppBarStoryboard;
    private Storyboard showCustomAppBarStoryboard;
    private Size appBarSize;
    private Size appBarButtonsSize;
    private bool isAppBarShown = true;

    private void CustomAppBarRoot_OnLoaded(object sender, RoutedEventArgs e)
    {
        appBarSize = new Size(CustomAppBarRoot.ActualWidth, CustomAppBarRoot.ActualHeight);
        appBarButtonsSize = new Size(ButtonsStackPanel.ActualWidth, ButtonsStackPanel.ActualHeight);
        InitializeStoryboards();

        HideCustomAppBar();
    }

    private void ShowCustomAppBar()
    {
        isAppBarShown = true;
        showCustomAppBarStoryboard.Begin();
    }

    private void HideCustomAppBar()
    {
        isAppBarShown = false;
        hideCustomAppBarStoryboard.Begin();
    }

    private void DotsTextBlock_OnTapped(object sender, TappedRoutedEventArgs e)
    {
        if (isAppBarShown)
            HideCustomAppBar();
        else
            ShowCustomAppBar();
    }

    private void InitializeStoryboards()
    {
        hideCustomAppBarStoryboard = new Storyboard();
        showCustomAppBarStoryboard = new Storyboard();

        var showDoubleAnimation = new DoubleAnimation()
        {
            EasingFunction = new CircleEase() {EasingMode = EasingMode.EaseInOut},
            To = 0,
            Duration = new Duration(TimeSpan.FromMilliseconds(200))
        };
        var hideDoubleAnimation = new DoubleAnimation()
        {
            EasingFunction = new CubicEase() {EasingMode = EasingMode.EaseInOut},
            To = appBarButtonsSize.Height,
            Duration = new Duration(TimeSpan.FromMilliseconds(200))
        };
        hideCustomAppBarStoryboard.Children.Add(hideDoubleAnimation);
        showCustomAppBarStoryboard.Children.Add(showDoubleAnimation);

        Storyboard.SetTarget(hideCustomAppBarStoryboard, CustomAppBarRoot);
        Storyboard.SetTarget(showCustomAppBarStoryboard, CustomAppBarRoot);
        Storyboard.SetTargetProperty(showCustomAppBarStoryboard, "(UIElement.RenderTransform).(TranslateTransform.Y)");
        Storyboard.SetTargetProperty(hideCustomAppBarStoryboard, "(UIElement.RenderTransform).(TranslateTransform.Y)");
    }

    #endregion

    private void CustomAppBarRoot_OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        var translateTransform = (CustomAppBarRoot.RenderTransform as TranslateTransform);

        double newY = e.Delta.Translation.Y + translateTransform.Y;
        translateTransform.Y = Math.Max(0, Math.Min(newY, appBarButtonsSize.Height));
    }

    private void CustomAppBarRoot_OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
    {
        // if small appbar-position changes are made app bar should back to previous position, just like in windows phone
        if (Math.Abs(e.Cumulative.Translation.Y) < 20)
            isAppBarShown = !isAppBarShown;

        if (!isAppBarShown)
            ShowCustomAppBar();
        else
            HideCustomAppBar();
    }

    private void CustomAppBarRoot_OnTapped(object sender, TappedRoutedEventArgs e)
    {
        e.Handled = true; // prevents raising Page.Tapped event so appbar won't be closed on AppBar-area tap
    }
}

Take in mind that above example is just "Proof-of-concept" - UI need to be improved.

fex
  • 3,488
  • 5
  • 30
  • 46
  • @MacaronLover i will edit in 20 minutes with example. – fex Jan 10 '15 at 21:16
  • Any luck with the example? – wbk727 Jan 10 '15 at 21:45
  • Brilliant - it works! Just one more thing, when the app bar is open how can I get the app bar to shrink to the collapsed position (small bar) when I tap anywhere on the screen outside of the app bar? – wbk727 Jan 11 '15 at 14:41
  • @MacaronLover edit done - just handle Page.Tapped event and set e.handled = true in AppBarGrid.Tapped event. Also added "check" for small changes in app bar Y position - app bar interaction is more natural. btw. if you think I helped you - you can accept the answer ;) – fex Jan 11 '15 at 15:15
  • For some reason the bar won't open when I tap the dots - btw I want to use this with a hub page so maybe some adjustments need to be made to it however please check my code. – wbk727 Jan 11 '15 at 19:21
  • You start out with `isAppBarShown = true` – Peter Torr - MSFT Jan 12 '15 at 00:05
  • OK I changed it to `isAppBarShown = false` and still nothing happens. However I know the problem relates to the fact that I'm using a Hub Page. Please have a look my updated XAML and tell me if anything needs to change. – wbk727 Jan 13 '15 at 18:29
  • @MacaronLover I'll take a look at that in few days – fex Jan 13 '15 at 22:11