Nico's digital footprint

I grew up in the nineties, that makes me awesome by default

Win Store: SuspensionManager failed

by Nico

While implementing tombstoning in a Windows Store app I recently ran into this issue. Some pages tombstoned and resumed perfectly, while some other pages gave this error when tombstoning

This one took me a while to figure out, but I finally found the problem. But before diving into the solution, I’ll show you how I got to this error.

Tombstoning

If you’re reading this, I’m assuming you know about the app lifecycle and tombstoning so I’m not going to explain that. To demonstrate the problem I created a blank app, added in a basic page so that Visual Studio included the NavigationHelper and SuspensionManager classes.

In the app I have two pages, Mainpage.xaml and Page2.xaml. Mainpage has a button that takes me to Page2. The navigation happens with the build-in NavigateToPageAction, easily dragged and dropped onto the button from Blend.

This creates an EventTriggerBehavior on the button that triggers a NavigateToPageAction

All that’s left to do is set the target page in the NavigateToPageAction properties

And we’re done. We’ve just created an app with a button that navigates to another page without writing any code, pretty neat and easy stuff right?

Next thing I did was follow this MSDN article that shows in a few easy steps how to implement tombstoning so that if the app tombstones on a certain page and the user switches back to the app, it reloads that page. It worked and all was well. That is, until I implemented the tombstoning on a page that was navigated to using the NavigateToPageAction. tombstoning that page always resulted in the error shown in the screenshot at the beginning of this post (and it took me a few hours to discover that the action was at fault here). Let’s have a closer look to see what’s actually happening here.

Navigation and NavigationParameters

When navigating between pages we can pass parameters. In Windows Store the signature of the Frame.Navigate function is this

Navigate(Type)
Navigate(Type, Object)
Navigate(Type, Object, NavigationTransitionInfo)

The parameter is of type Object, meaning we can pass in anything we’d like. Pretty powerful right? Right, except when tombstoning.

At a certain point in the Tombstoning process this snippet of code gets executed

Code Snippet
  1. private static void SaveFrameNavigationState(Frame frame)
  2. {
  3.     var frameState = SessionStateForFrame(frame);
  4.     frameState["Navigation"] = frame.GetNavigationState();
  5. }

This will save the current NavigationState of the pages into a dictionary. The caveat here is that this only works with primitive types. If we pass strings or integers as parameter this will work, no problem. If we pass in an object of type MyClass we will get the SuspensionManager failed error.

Okay cool, that’s all great, we found a possible source of the error. But, we’re not passing in any parameters to the page, so why are we getting the error? Let’s set a breakpoint in the OnNavigatedTo method in Page2.xaml.cs

Well well, would you look at that. Our NavigateToPageAction snuck in a little parameter without telling us, and that parameter is not a primitive type so we get a crash.

The fix + conclusion

The fix is actually really easy, don’t use NavigateToPageAction. Yes just don’t use it. I know it’s really fast and easy but it breaks the entire tombstoning thingy. I fixed it by calling Frame.Navigate (Type) from the button’s click event.

I’ve uploaded a sample with two buttons on my OneDrive, one button has the crashing effect, the other one doesn’t.

Download Link


Tags:

.Net | Windows 8 | WinRT | XAML | tombstoning | lifecycle

A custom button that disappears in snapped mode

by Nico

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" />

Conclusion

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.

You can find a demo application using both ways of working on my OneDrive (Top appbar uses first type, bottom appbar uses second type).


Tags:

.Net | WinRT | Windows 8 | XAML

  Log in

About the author

Hi,

My name is Nico, I’m an MVP Windows Platform Development living in Belgium.
I’m currently employed as a .NET consultant at RealDolmen, one of Belgium’s leading IT single source providers.

I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone development. If you're in Belgium feel free to drop by if we're doing an event. http://www.madn.be

Since June 2012 I'm a proud member of Microsoft's Extended Experts Team Belgium. And in February 2013 I became a member of DZone's Most Valuable Bloggers family.

In 2013 I became a book author and wrote "Windows 8 app projects, XAML & C# edition".

In 2014 I received the MVP award for the very first time.

I hope to get feedback from my readers either through comments, mail (nico_vermeir@hotmail.com), twitter, facebook, …

 

mvp

 

mvp

 

 

MeetLogo

 

MVBLogo

mybook

 

Month List