With Windows Phone 8.1 Microsoft drastically changed on of the basic chassis requirements in Windows Phone since day 0. The hardware buttons at the bottom of the phone. There were many reasons and one of them was the ability to use same chassis for both Android and Windows Phone devices. This many cited was very important smaller OEMs that recently signed up to Windows Phone ecosystem.
WP8 resolutions (and scaling factors as reported by Silverlight apps):
- 800 x 480 (1.0)
- 1280 x 720 (1.5)
- 1280 x 768 (1.6)
- 1920 x 1080 (2.25)
For Silverlight Windows Phone app (most of the apps that is) the resolution defaults to 800 x 480. This fits nicely with Scaling factor 1.6 which was used by many early Nokia devices. The scaling factor 1.5 however gave 853 x 480. This primarily affected HTC 8 S/X devices and Samsung ATIV S devices only. Later when Lumia 1520 came out, it uses 1020 and that used 2.25 and that also had 853 x 480 res.
What this meant was that WP7 and any WP8 apps that fix their resolution would leave 53 px gap. With my 1Shot camera app, I have a complicated control structure and I ended up fixing many heights and widths.. Not only that I also move certain objects on
OrientationChanged using specific values of
TranslateY. Last week after a user complaint, I added code to detect 720p and 1080p resolutions and to stretch those by additional 53px. However I never tested this on newer 720p devices that no longer have hardware buttons.
What Windows Phone Dev team did was added another system control like
NavigationBar. Unlike ApplicationBar, NavigationBar isn’t accessible from within application. Playing with emulator set to show NavigationBar did not show any change in size reported by
double width = Application.Current.Host.Content.ActualWidth;
double height = Application.Current.Host.Content.ActualHeight;
After much complaining around twitter I came across windows phone 8.1 hide NavigationBar post on MSDN Forums. One of the suggestions was using
PhoneApplicationFrame.FullScreen. Setting it to true or false made no difference.. none at all. After another round of complains, I came across this post Layout and the Windows Phone navigation bar.. it suggest that
SizeChanged event should take care of it and
ActualHeight properties will sort it out.
Being not particularly clever, I thought that Page’s SizeChanged would be fired.. sure it was firing but ActualWidth and ActualHeight were 0.0. Width and Height were NaN so that was useless. Thinking that I could do better than that, I tried using SizeChanged exposed by Application.Current.Host.Content.. nope no difference.. it wasn’t a particularly bright idea.. Yesterday I spent time deliberating whether I should use hardcoded lists to indicate NavigationBar availability or not.. I already do so for high resolution sensors.. nah too much hassle
So today I said.. surely there must be something… on SizeChanged event handler of page, I scanned App.RootFrame.. I found something.. a private field called _visibleRegion correctly showed that 53 px at the botton were not available on 720p emulator when NavigationBar was visible. I think I am clever than most…
private Thickness GetMargin()
Thickness t = new Thickness();
var field = App.RootFrame.GetType().GetField("_visibleRegion", BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
object visibleRegion = field.GetValue(App.RootFrame);
if (visibleRegion != null && visibleRegion is Thickness)
t = (Thickness)visibleRegion;
This would get me the right margin and I could just just correct a few bits and bingo.. hahah yah right..
FieldAccessExpcetion eat that you clever git.. Apps don’t run under full trust on phone and I couldn’t reflect to this level. Sigh.. This was hard.. why had Windows Phone team gone way out of their way to piss developers off ?? Having almost lost all hope, I thought, let me try subscribe to Page’s LayoutRoot grid’s SizeChanged event… had a look there.. event handler exposed
NewValue and they showed the correct values!!! yay
double gridWidth = e.NewSize.Width > e.NewSize.Height ? e.NewSize.Width : e.NewSize.Height;
double gridHeight = e.NewSize.Width > e.NewSize.Height ? e.NewSize.Height : e.NewSize.Width;
double w = Application.Current.Host.Content.ActualWidth;
double h = Application.Current.Host.Content.ActualHeight;
double hostWidth = w > h ? w : h;
double hostHeight = w > h ? h : w;
bool hasSoftButtons = hostWidth > gridWidth;
Just for your sanity.. I always want Landscape mode so width is always greater than height based on supported resolutions.. so there you have it.. subscribe to LayoutRoot’s SizeChanged and get the right size.. OnResize, the dimensions reflect 800 x 480 resolution and not 853 x 480.