a custom button that disappears in snapped mode

I’m currently working on a Windows 8.1 app that has quite some buttons in the app bar. The buttons are not positioned next to each other in one row and are not all the same size. This means that I can’t use the new CommandBar to auto-hide some buttons whenever the app gets snapped to the side. That means that it’s time for a custom button.

Actually, I have two solutions to this problem. One that puts the hide/show functionality in the button and one that puts it in the page where you use the appbar.

The one with functionality in the button

Create a new class and let it derive from Button.

Code Snippet
  1. public class CustomAppBarButton : Button
  2. {
  3.     public static readonly DependencyProperty HideOnSnapProperty = DependencyProperty.Register(
  4.         "HideOnSnap", typeof (bool), typeof (CustomAppBarButton), new PropertyMetadata(default(bool)));
  5.  
  6.     public bool HideOnSnap
  7.     {
  8.         get { return (bool) GetValue(HideOnSnapProperty); }
  9.         set { SetValue(HideOnSnapProperty, value); }
  10.     }
  11.  
  12.     public CustomAppBarButton()
  13.     {
  14.         HideOnSnap = false;
  15.  
  16.         Window.Current.SizeChanged += CurrentOnSizeChanged;
  17.     }
  18.  
  19.     private void CurrentOnSizeChanged(object sender, WindowSizeChangedEventArgs windowSizeChangedEventArgs)
  20.     {
  21.         if (!HideOnSnap)
  22.             return;
  23.  
  24.         var size = Window.Current.Bounds;
  25.  
  26.         if (size.Width <= size.Height)
  27.         {
  28.             Visibility = Visibility.Collapsed;
  29.         }
  30.         else
  31.         {
  32.             Visibility = Visibility.Visible;
  33.         }
  34.     }
  35. }

First thing we need is a dependency property so we can specify whether or not we want to hide a button in snapped view. From the constructor we subscribe to the SizeChanged event of the current window. Whenever that event fires every button will check if it needs to hide. If HideOnSnap is enabled, the buttons will check if the app is currently snapped by checking if the width of the window is smaller than the height.

To use this control, and let the button hide on snap you can use something like this

Code Snippet
  1. <local:CustomAppBarButtonType1 Content="Button1" HideOnSnap="False" />
  2. <local:CustomAppBarButtonType1 Content="Button2" HideOnSnap="True" />

This works peachy, but if you have an appbar with multiple of these buttons and run on a slower system you might experience some delay when opening up the appbar for the first time. All buttons are getting created and are attaching the event handler so depending on the amount of buttons it might take some time.

If you experience the delay, option 2 might work better

The one where the page handles it

This option also needs a custom button class but it’s a much smaller one

Code Snippet
  1. public class CustomAppBarButton : Button
  2. {
  3.     public static readonly DependencyProperty HideOnSnapProperty = DependencyProperty.Register(
  4.         "HideOnSnap", typeof (bool), typeof (CustomAppBarButton), new PropertyMetadata(default(bool)));
  5.  
  6.     public bool HideOnSnap
  7.     {
  8.         get { return (bool) GetValue(HideOnSnapProperty); }
  9.         set { SetValue(HideOnSnapProperty, value); }
  10.     }
  11.  
  12.     public CustomAppBarButton()
  13.     {
  14.         HideOnSnap = false;
  15.     }
  16. }

No event handling in this class, just a dependency property to specify whether or not it needs to hide. The actual hiding and showing of the buttons happens in the page itself.

Code Snippet
  1. public MainPage()
  2. {
  3.     InitializeComponent();
  4.     SizeChanged += OnSizeChanged;
  5. }
  6.  
  7. private async void OnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
  8. {
  9.     var size = Window.Current.Bounds;
  10.     var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
  11.  
  12.     if (size.Width <= size.Height)
  13.     {
  14.         await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, HideAllHideableButtons);
  15.     }
  16.     else
  17.     {
  18.         await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, ShowAllHideableButtons);
  19.     }
  20. }

We register for the SizeChanged event of the page (event comes from the FrameworkElement class, a page in Windows 8.1 derives from that class). The rest of the code is very similar from the code in the buttons. A difference is that we need to use the dispatcher here to marshall the hiding / showing of the buttons to the UI thread.

Here’s the code to hide all hideable buttons (note that you have to name your appbar, mine is called AppBarBottom

Code Snippet
  1. private void HideAllHideableButtons()
  2. {
  3.     var buttons = AppBarBottom.FindDescendantsByType<CustomAppBarButtonType2>().Where(b => b.HideOnSnap);
  4.  
  5.     foreach (var appBarButton in buttons)
  6.     {
  7.         appBarButton.Visibility = Visibility.Collapsed;
  8.     }
  9. }

We’re looking for all controls on the page that are of type CustomAppBarButton and that have HideOnSnap set to true (don’t worry about the FindDescendantsByType function, that’s an extension method, I’ll come back to it in a minute).

Every button that we found, we set to collapsed and voila, buttons are hidden when snapped. Getting them to show up again is very (very) similar

Code Snippet
  1. private void ShowAllHideableButtons()
  2. {
  3.     var buttons = this.FindDescendantsByType<CustomAppBarButton>().Where(b => b.HideOnSnap);
  4.  
  5.     foreach (var appBarButton in buttons)
  6.     {
  7.         appBarButton.Visibility = Visibility.Visible;
  8.     }
  9. }

As for the FindDescendantsByType<T> extension method:

Code Snippet
  1. public static IEnumerable<T> FindDescendantsByType<T>(this DependencyObject depObj) where T : DependencyObject
  2. {
  3.     if (depObj != null)
  4.     {
  5.         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
  6.         {
  7.             DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
  8.             if (child != null && child is T)
  9.             {
  10.                 yield return (T)child;
  11.             }
  12.  
  13.             foreach (T childOfChild in FindDescendantsByType<T>(child))
  14.             {
  15.                 yield return childOfChild;
  16.             }
  17.         }
  18.     }
  19. }

this will look through the visual tree in search for elements of the specified type (I think I found this method on SO somewhere, forgot the exact link but this is not my code).

Usage is exactly the same as with the first type of button.

Code Snippet
  1. <local:CustomAppBarButtonType2 Content="Button1" HideOnSnap="False" />
  2. <local:CustomAppBarButtonType2 Content="Button2" HideOnSnap="True" />
          <h2>Conclusion</h2>  <p>In this post I’ve discussed two ways of creating a custom button that can hide itself when the application gets snapped to the side, allowing for a quick way to create an adaptable application bar.</p>  <p>You can find a demo application using both ways of working on my <a href="http://1drv.ms/1mvkni9" target="_blank">OneDrive</a> (Top appbar uses first type, bottom appbar uses second type).</p>

This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.

Leave a Comment