Donnerstag, 28. Oktober 2010

WindowsPhone 7: NavigationService.Back raises Exception

Sometime a NavigationService.Back call could raise an exception, if the user tombstoned your Windows phone 7 app and you try to call a Navigations.Back after this.

Possible szenario: You ask the user “Do you really want to delete this item?”. After returning a OK from your MessageBox you delete the item and in all cases you want to return from your detail page to your list page with NavigationService.Back.  To avoid a exception you have to put your NavigationService.Back call in a try..catch statement, because the user could press the windows button at your Messagebox and after this the CLR will raises a Cancel for your messagebox and executes the rest of your code with the NavigationService.Back call. Guess from me: Because this situation is not handled by the Silverlight for Windows Phone framework a exception will occur.

Trauriges SmileyTrauriges SmileyTrauriges Smiley

Windows Phone 7: Tombstoning and Messagebox

For a good usabilty for your Windows Phone 7 app you should consider to save your Messagebox text as transient data for example with help of the PhoneApplicationService.Current.State Dictionary. Why is this necessary? For example if the user gets a call or wants to switch to a other ap by pressing the windows button and have not read the Messagbox the user should shown it again after he return to your app.

We use in our project the following class which do all the work for us.

The Show-methods wrapps the orginal MessageBox-Show methods (one override is missing, because a show method with return value for questions make no sense. After tombstoning you loose your functional context, so this Messagebox type should be handled). The ShowFromTransientValues-Method is the key for the suggested behaviour and can be called after you bring your application in a proper state – for example in the first PageLayoutUpdated-Event after reactivation.

HINT: The MessageBox.Show method will return in case of tombstoning always a cancel value. That is a fact that you should keep in mind for Windows Phone 7 programming.

   1: public static class TransientMessageBox
   2:     {
   3:         const string MessageTextKey = "TransientMessageBoxText";
   4:         const string MessageCaptionKey = "TransientMessageBoxCaption";
   5:  
   6:         public static void ShowVirtual(string messageBoxText)
   7:         {
   8:             TransientStorageHelper.AddOrUpdate(MessageTextKey, messageBoxText);
   9:         }
  10:  
  11:         public static void ShowVirtual(string messageBoxText, string caption)
  12:         {
  13:             TransientStorageHelper.AddOrUpdate(MessageTextKey, messageBoxText);
  14:             TransientStorageHelper.AddOrUpdate(MessageCaptionKey, caption);
  15:         }
  16:  
  17:         public static void Show(string messageBoxText)
  18:         {
  19:             ShowVirtual(messageBoxText);
  20:  
  21:             var result = MessageBox.Show(messageBoxText);
  22:  
  23:             Debug.WriteLine("Transient MessageBox result:" + result);
  24:  
  25:             if (result == MessageBoxResult.OK)
  26:             {
  27:                 RemoveTextKey();
  28:             }
  29:         }
  30:  
  31:         public static void Show(string messageBoxText, string caption)
  32:         {
  33:             ShowVirtual(messageBoxText, caption);
  34:  
  35:             var result = MessageBox.Show(messageBoxText, caption, MessageBoxButton.OK);
  36:  
  37:             Debug.WriteLine("Transient MessageBox result:" + result);
  38:  
  39:             if (result == MessageBoxResult.OK)
  40:             {
  41:                 RemoveAllKeys();
  42:             }
  43:         }
  44:  
  45:         private static void RemoveAllKeys()
  46:         {
  47:             TransientStorageHelper.Remove(MessageTextKey);
  48:             TransientStorageHelper.Remove(MessageCaptionKey);
  49:         }
  50:  
  51:         private static void RemoveTextKey()
  52:         {
  53:             TransientStorageHelper.Remove(MessageTextKey);
  54:         }
  55:  
  56:         public static void ShowFromTransientValues()
  57:         {
  58:             if (!PhoneApplicationService.Current.State.ContainsKey(MessageTextKey)) return;
  59:  
  60:             if (PhoneApplicationService.Current.State.ContainsKey(MessageCaptionKey))
  61:             {
  62:                 if (MessageBox.Show(TransientStorageHelper.GetValue<string>(MessageTextKey),
  63:                                 TransientStorageHelper.GetValue<string>(MessageCaptionKey),
  64:                                 MessageBoxButton.OK) == MessageBoxResult.OK)
  65:                 {
  66:                     RemoveAllKeys();
  67:                 }
  68:             }
  69:             else
  70:             {
  71:                 if (MessageBox.Show(TransientStorageHelper.GetValue<string>(MessageTextKey)) == MessageBoxResult.OK)
  72:                 {
  73:                     RemoveTextKey();
  74:                 }
  75:             }
  76:         }
  77:     }

Montag, 18. Oktober 2010

Unittest: Testing non-public methods

Sometimes you want to write unit tests against methods that aren't publicly exposed. Rather than make these private methods public, which is a bad idea, you can specify friendly assemblies to which your methods will be exposed.

All you have to do is edit the AssemblyInfo.cs of the assembly you want to expose to your tests and add the following line ("SampleTestProject" is the name of the test assembly):

[assembly: InternalsVisibleTo("SampleTestProject")]
After this change in the assembly.cs and a rebuild, your “internal” signed methods will be exposed.

Samstag, 16. Oktober 2010

Windows Phone 7: Track your memory usage

One of the restrictions of a Windows Phone 7 app is the maximum memory consumption of 90 MB (peak value). To check this you need to write your own memory usage monitor. You can use the follwing memory usage control, which can be started in the constructor of the app (optional with or without garbage collection).

   1: // <summary>
   2:     /// Helper class for showing current memory usage
   3:     /// </summary>
   4:     public static class MemoryDiagnosticsControl
   5:     {
   6:         static Popup _popup;
   7:         static TextBlock _currentMemoryBlock;
   8:         static DispatcherTimer _timer;
   9:         static bool _forceGc;
  10:  
  11:         /// <summary>
  12:         /// Show the memory counter
  13:         /// </summary>
  14:         /// <param name="forceGc">Whether or not to do automatic garbage collection each tick</param>
  15:         public static void Start(bool forceGc)
  16:         {
  17:             _forceGc = forceGc;
  18:  
  19:             CreatePopup();
  20:             CreateTimer();
  21:             ShowPopup();
  22:             StartTimer();
  23:         }
  24:  
  25:         /// <summary>
  26:         /// Stop the memory counter
  27:         /// </summary>
  28:         public static void Stop()
  29:         {
  30:             HidePopup();
  31:             StopTimer();
  32:         }
  33:         /// <summary>
  34:         /// Show the popup
  35:         /// </summary>
  36:         static void ShowPopup()
  37:         {
  38:             _popup.IsOpen = true;
  39:         }
  40:  
  41:         static void StartTimer()
  42:         {
  43:             _timer.Start();
  44:         }
  45:  
  46:         static void CreateTimer()
  47:         {
  48:             if (_timer != null)
  49:                 return;
  50:  
  51:             _timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(300)};
  52:             _timer.Tick += TimerTick;
  53:         }
  54:  
  55:         static void TimerTick(object sender, EventArgs e)
  56:         {
  57:             // call Garbage collector before getting memory usage
  58:             if (_forceGc)
  59:                 GC.Collect();
  60:  
  61:             var mem = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
  62:             _currentMemoryBlock.Text = string.Format("{0:N}", mem / 1024);
  63:         }
  64:  
  65:         static void CreatePopup()
  66:         {
  67:             if (_popup != null)
  68:                 return;
  69:  
  70:             _popup = new Popup();
  71:             var fontSize = (double)Application.Current.Resources["PhoneFontSizeSmall"] - 2;
  72:             var foreground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
  73:             var sp = new StackPanel { Orientation = Orientation.Horizontal, Background = (Brush)Application.Current.Resources["PhoneSemitransparentBrush"] };
  74:             _currentMemoryBlock = new TextBlock { Text = "---", FontSize = fontSize, Foreground = foreground };
  75:             sp.Children.Add(new TextBlock { Text = "Mem(kB): ", FontSize = fontSize, Foreground = foreground });
  76:             sp.Children.Add(_currentMemoryBlock);
  77:             sp.RenderTransform = new CompositeTransform { Rotation = 90, TranslateX = 480, TranslateY = 420, CenterX = 0, CenterY = 0 };
  78:             _popup.Child = sp;
  79:         }
  80:  
  81:         static void StopTimer()
  82:         {
  83:             _timer.Stop();
  84:         }
  85:  
  86:         static void HidePopup()
  87:         {
  88:             _popup.IsOpen = false;
  89:         }
  90:     }

Windows Phone 7: Disable Screensaver (lock) during action

In our Windows Phone 7 project we need to disable the screensave (lock) of Windows Phone 7 for example during playing a media file in Mediaelement or during a picture slideshow. To reach this goal you can use the PhoneApplicationService.Current.UserIdleDetectionMode and set it to IdleDetectionMode.Enabled or IdleDetectionMode.Disabled.

 

Sample of a property

   1: public bool SlideshowIsActive
   2:        {
   3:            get { return _slideshowIsActive; }
   4:            set
   5:            {
   6:                if (_slideshowIsActive == value) return;
   7:  
   8:                _slideshowIsActive = value;
   9:  
  10:                PhoneApplicationService.Current.UserIdleDetectionMode = _slideshowIsActive ? IdleDetectionMode.Disabled : IdleDetectionMode.Enabled;
  11:  
  12:                NotifyPropertyChanged(() => SlideshowIsActive);
  13:            }
  14:        }