I recently had the opportunity to begin exploring and toying around with Windows Phone 8 development.
Why, you ask? Why not really, but mostly because I was giving a free phone and it's an untapped market when it comes to apps. At this stage, it's not over diluted like the Android and iPhone stores are. That and of course, there is an option to create apps using HTML, JavaScript, and CSS technologies that I've used and mastered for years!
I actually submitted my first app last weekend and sadly it got rejected. :( I was given two reasons; firstly I did set a default application icon, whoops my bad. Secondly, I didn't properly handle the back button since there are multiple pages in my application.
It took me several hours to finally find the answer so I thought I would share it. Please note, this feels like a bit of a hack, but I wasn't able to get the suggested solutions working.
Of course, now that I have the solution, it feels quite simple, still hacky, but simple. It also opened my eyes up to a lot of future potential, specifically the way I am about to demonstrate sending data between C# and JavaScript!
The end solution involves two key functions; the C# piece:
Browser.InvokeScript("MyJavascriptFunction");
And the JavaScript piece:
window.external.notify(myJavascriptVariable);
I will explain how to solve the issue in more detail in a second, but wanted to first present the key functions and their use.
The C# function does exactly what the name says, it invokes a script. Now, here is the part that feels hacky. According to the MSDN documentation here, the function is supposed to return an object that contains the results of the JavaScript execution. I tried for the life of me to get this to work, but my result was always empty.
So please, if you know what I did wrong here, I would love to know as the following part goes on to explain my hacked together solution to get the job done as they say.
The second key function is a JavaScript call that once again does exactly what it says, it notifies an external device with the data I passed in.
On to the final solution.
Your XAML file that contains your web embedded web browser requires a few updates, typically this is your MainPage.xaml. When you create a new project, Visual Studio will probably generate XML quite similar to this:
<!---->
<Grid x:Name="LayoutRoot" Background="Transparent">
<phone:WebBrowser x:Name="Browser"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Loaded="Browser_Loaded"
NavigationFailed="Browser_NavigationFailed"/>
</Grid>
Two additional variables are required to be added and set after the NavigationFailed
variable:
<!---->
<Grid x:Name="LayoutRoot" Background="Transparent">
<phone:WebBrowser x:Name="Browser"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Loaded="Browser_Loaded"
NavigationFailed="Browser_NavigationFailed"
IsScriptEnabled="True"
ScriptNotify="ScriptNotifyHandler"/>
</Grid>
The two variables are IsScriptEnabled
and ScriptNotify
. The first function ensures JavaScript execution is allowed and the second variable defines a function that must be added in your MainPage.xaml.cs file that will be called whenever you execute a call with window.external.notify
.
Once these variables are added, let's add the new function in our MainPage.xaml.cs file:
private void ScriptNotifyHandler(object sender, NotifyEventArgs e)
{
}
With the key places all in place, here is the full solution that I implemented. Once again in our MainPage.xaml.cs file, we need to capture the back button press:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
Browser.InvokeScript("checkClose");
e.Cancel = true;
}
This function does two important things. It invokes a JavaScript function checkClose
and it sets e.Cancel
to true
. This will cause the application to not exit when the user presses the back button.
Now here is the JavaScript checkClose
function:
var state;
function checkClose() {
if ($startScreen.is(":visible")) {
state = "home";
} else {
state = "game";
}
try {
window.external.notify(state);
} catch(err) {
alert(err);
}
}
This will require some customization by you to get it working in your example.
In my example above, the goal of this function is to set and send a value for the global state
variable. If the user is on the start screen of my application, I set state
to home
; otherwise, I set it to game
. This data is then passed to C#.
Two more things before this is a finished solution, we go back to the MainPage.xaml.cs file and fill in the blanks to our ScriptNotifyHandler
function:
private void ScriptNotifyHandler(object sender, NotifyEventArgs e)
{
if (e.Value == "game")
{
Browser.InvokeScript("GoHome");
}
else
{
Application.Current.Terminate();
}
}
If the value sent contains the word game, I once again invoke another JavaScript function; otherwise, I terminate the application because the user is on the start screen.
The final piece is creating the JavaScript function GoHome
. This function should be created by you to bring the user back to your start screen or if you do not always want to send the user directly back to the start page, you can invoke any other JavaScript to bring the user back to their previous spot.
I hope this helps you and if someone can help me make this solution feel less hacky, that would be cool too.
On a side/final note, I resubmitted my app this past weekend, so hopefully it will get accepted this time. :)