A group blog from members of the VB team
This is Part 5 of the "VB Universal Windows App" series:
So far we've been able to re-use all our code and XAML by placing them in the PCL. Actually, PCLs can only ever contain calls to APIs that are common to the platforms they target. This generally isn't a problem, because most APIs on Windows also exist on Windows Phone, and vice versa.
Today we'll explore how to call platform--specific APIs from Common code. In particular, we'll make it so our common main page can hide the Windows Phone statusbar (with connection quality, battery, clock) - when it's running on Windows Phone, of course. There's nothing that needs hiding on Windows.
We'll use a technique known as "Inversion of Control" (IOC). It sounds complicated but is easy in practice. Advanced developers frequently use IOC for unit-testing as well.
Step 1: Declare a new interface "IPlatformAbstraction" in App1_Common > App.vb, and a public field of that type.
Public Interface IPlatformAbstraction Function SetStatusbarVisibilityAsync(isVisible As Boolean) As Task End Interface Public Class App Public Shared Platform As IPlatformAbstraction .... End Class
Step 2: Any time you want to play audio from within App1_Common, do so via the platform abstraction layer:
Step 3: Now you have to provide two implementations of the interface, one in App1_Windows, and one in App1_Phone. I chose to provide it inside my two App.xaml.vb files. Remember also to assign "App1_Common.App.Platform" inside the OnLaunched method.
NotInheritable Class App Inherits Application Implements App1_Common.IPlatformAbstraction Public Async Function SetStatusbarVisibilityAsync(isVisible As Boolean) Implements IPlatformAbstraction.SetStatusbarVisibilityAsync ' TODO: implement this! End Sub Protected Overrides Sub OnLaunched(e As LaunchActivatedEventArgs)< App1_Common.App.Platform = Me .... End Sub .... End Class
Look at these screenshots. (To test how my app behaves with "software-buttons" I launched either the 720p or 1080p emulators, clicked on >> then Sensors, and turned on software buttons).
The screenshot on the left used ApplicationViewBoundsMode.UseVisible. You can see that it calculated the layout rectangle of my page based only on what's visible, after you take away the space for software-buttons and status-bar.
The screenshot on the right used ApplicationViewBoundsMode.UseCoreWindow. You can see that it calculated the layout rectangle of my page based on the full screen size, regardless of software-buttons and status-bar.
The status-bar visibility (at the top) is under the app's control. The app can show or hide it. Showing and hiding takes about half a second, since it's accompanied by a nice animation. These are the two methods you can use:
Await StatusBar.GetForCurrentView().ShowAsync() Await StatusBar.GetForCurrentView().HideAsync()
The software-button visibility (at the bottom) is under the user's control. And most phones come with hardware buttons, not software buttons, so they don't even show these.
Step 4: Implement the SetStatusbarVisibility method of our Platform Abstraction Layer:
' PHONE IMPLEMENTATION, in App1_Phone > App.xaml.vb Public Async Function SetStatusbarVisibilityAsync(isVisible As Boolean) Implements IPlatformAbstraction.SetStatusbarVisibilityAsync If isVisible Then Await StatusBar.GetForCurrentView().ShowAsync()>br/> Else Await StatusBar.GetForCurrentView().HideAsync() End If End Sub ' WINDOWS VERSION, in App1_Windows > App.xaml.vb Public Async Function SetStatusbarVisibilityAsync(isVisible As Boolean) Implements IPlatformAbstraction.SetStatusbarVisibilityAsync If False Then Await Task.Delay(0) ' dummy to silence the compiler warning End Sub
There's one final tweak I want to mention. It's inside App1_Common > AdaptiveMainPage.xaml.vb > OnNavigatedTo that I want to call SetStatusbarVisibilityAsync.
But hiding the statusbar has a half-second animation, and I don't want to delay my OnNavigatedTo method while waiting for it to finish. In effect, I want to just kick off the async method in a fire-and-forget manner.
Here is a nice idiom for fire-and-forget asyncs. It uses an extension method "FireAndForget" which I wrote. It silences the compiler warning ("hey you're not awaiting this async method"). And it demonstrates to anyone reading my code what I'm trying to do:
And here's how I implement that FireAndForget extension method:
Module AsyncHelpers <Extension> Async Sub FireAndForget(task As Task) Try Await task Catch ex As Exception App.SendErrorReport(ex) End Try End Sub End Module
Actually, in practice, I also often write two additional FireAndForget overloads, to make it easy to use with asynchronous WinRT APIs. They're implemented exactly the same way:
<Extension> Async Sub FireAndForget(task As IAsyncAction) <Extension> Async Sub FireAndForget(Of T)(task As IAsyncOperation(Of T))
We've now created a complete VB Universal app - although under the hood, a universal app is really two separate apps. In this blog series we've used two techniques for sharing code between the two apps. We've been able to share most of our code, XAML and assets by putting them inside a PortableClassLibrary. And we've used Inversion Of Control (IOC) to let the PCL call into platform-specific APIs.
Good luck, as you go out and build your own VB Universal Apps! It's an open app-store out there, ready for you to make your impact.
And stay tuned for tomorrow's blog post, with complete source code for the minimal "App1.vb" that we've been creating so far, and also complete source code for a fully-fledged universal VB game "Breakout Universal".