World of Warcraft API Starter Kit for Windows Phone

 

Via DaveDev.net

 

AllRealms[1]SingleRealmAndMenu[1]settings[1]

Overview

In my previous post I gave you an overview of my teams Starter Kits for Windows Store Apps.  I recently took the World of Warcraft Starter Kit I did for Windows, which was written in WinJS (Windows Library for JavaScript), and ported it to Windows Phone.

Since there is currently no WinJS available for Windows Phone I chose to write a XAML/C#  native Windows Phone app and then make my network calls using the Web Browser Control.  This is the same common approach that is used in the Windows Phone HTML5 App template and other popular tools such as Intel’s XDK.

In case you missed the previous post my reasons for choosing Blizzard’s World of WarCraft APIs was to create an understandable sample app to display Realm Status (these are the game servers for World of Warcraft).  For those not in the know World of Warcraft is a very successful online game from Blizzard Entertainment® and offers an excellent open api to get game statistics.  The full API documentation can be found at https://blizzard.github.io/api-wow-docs/.

You can grab the Phone version of the Starter Kit here on Github and I have also added it to my Starter Kits section on this blog.

Currently I have implemented the WOW API into two core pieces of functionality in the Windows Phone version.

  • Realm Status (individual)
  • Realm Status (all)

Just like the Windows Store App I wanted the Windows Phone version to serve as a workable app template for helping your own apps pass certification.  Taken what I’ve learned from my own apps I’ve implemented several pieces of functionality I’ve seen developers fail certification on or get tripped up with during development.  This sample app includes workable functionality for all of the following:

    • Application Bar
    • Settings Page
    • About Page
    • Web Browser Control
    • Web Tasks
    • Calling HTML5/JS from C#
    • Small and Medium Tiles
Requirements
  • Windows 8
  • Visual Studio 2012 Express for Windows Phone or higher
Setup

Customization

Step 1. All of the Blizzard World of Warcraft API's do not require a developer key to use. However, if you plan on creating an application with heavy api usage Blizzard requests you contact them at api-support@blizzard.com to register your application.

Step 2. Currently only Realm Status is implemented. Adding additional functionality such as character or pvp info is as easy as calling the appropriate wow api (found in https://blizzard.github.io/api-wow-docs/) and then wrapping it in a function the same way realm status was done in /html/js/wowapi.js and /html/js/getRealmStatus.

Wowapi.js file contains the function call to get all of the realm statuses and then generated a bunch of div tags inside of a div in /html/index.html called divStatus.  In the Windows Store version we used WinJS data convertors to change the value of the colors in the listview columns.  For this version we are using simple CSS to color the background of each div.

  1: "use strict";
  2:  
  3: function getRealmStatusAll() {
  4:  
  5:     $.getJSON("https://us.battle.net/api/wow/realm/status?jsonp=?", function (data) {
  6:         $.each(data.realms, function (i, item) {
  7:  
  8:             //Add some padding between the server name cells
  9:             var bgPadding = 'padding-top: 30px;padding-bottom: 30px;';
  10:  
  11:             //Check status of realm and convert background to green for online and red for offline
  12:             var bgStyle = item.status === true ? 'background-color: green;' : 'background-color: red;';
  13:  
  14:             //Set up div style and show server text
  15:             var divText = "<div style='' + bgPadding + bgStyle + "'><p>" + item.name + "<p></div><p>";
  16:  
  17:             //Add new div to main divStatus div
  18:             $(divText).appendTo("#divStatus");
  19:         });
  20:     });
  21: }
  22:  
  23: function onLoad() {
  24:     //tmp until buttons included
  25:     getRealmStatusAll();
  26: }

GetRealmStatus.js follows similar convention to wowapi.js but with the exception that we will now accept an argument for a single server.  This argument will actually be passed to the JavaScript function from the C# code the Web Browser Control is hosted in.

  1: "use strict";
  2:  
  3: function getRealmStatus(realm) {
  4:      $.getJSON("https://us.battle.net/api/wow/realm/status?realm=" + realm + "&jsonp=?", function (data) {
  5:         $.each(data.realms, function (i, item) {
  6:  
  7:             //Add some padding between the server name cells
  8:             var bgPadding = 'padding-top: 30px;padding-bottom: 30px;';
  9:  
  10:             //Check status of realm and convert background to green for online and red for offline
  11:             var bgStyle = item.status === true ? 'background-color: green;' : 'background-color: red;';
  12:  
  13:             //Set up div style and show server text
  14:             var divText = "<div style='' + bgPadding + bgStyle + "'><p>" + item.name + "<p></div><p>";
  15:  
  16:             //Add new div to main divStatus div
  17:             $(divText).appendTo("#divStatus");
  18:         });
  19:     }); 
  20:  
  21:     //alert(realm); //To Test args
  22: }
  23:  
  24:  

Step 3. Now that we have the networking calls setup (with minimal change from our WinJS Windows Store version) we need some way to display the HTML that we are generating.

This is where we will take the Web Browser Control and tell it to load the appropriate content.  We are also using a flag here for what type of data we need to display (All or Single realms).  These flags are set in the Application scope and available from all the app’s pages.  So when a user sets a new server to track we will see those changes reflected immediately.

  1: // Url of Home page
  2:         private string uriRealmsAll = "/Html/index.html";
  3:         private string uriRealmsSingle = "/Html/realmStatus.html";
  4:      
  5:         // Constructor
  6:         public MainPage()
  7:         {
  8:             InitializeComponent();
  9:             InitSettings();
  10:  
  11:             Browser.IsScriptEnabled = true;
  12:         }
  13:  
  14:         //Get server settings
  15:         private void InitSettings() 
  16:         {
  17:             if (App.appSettings.Contains("realm"))
  18:             {
  19:                 App.userRealm = (string)App.appSettings["realm"];
  20:             }
  21:             else
  22:             {
  23:                 App.userRealm = App.defaultRealm;
  24:                 App.appSettings.Add("realm", App.defaultRealm);
  25:             }
  26:         }
  27:  
  28:         private void Browser_Loaded(object sender, RoutedEventArgs e)
  29:         {
  30:             if (App.allRealms) 
  31:             {
  32:                 Browser.Navigate(new Uri(uriRealmsAll, UriKind.Relative));
  33:             }
  34:             else
  35:             {
  36:                 Browser.Navigate(new Uri(uriRealmsSingle, UriKind.Relative));
  37:             }
  38:  
  39:         }
  40:  
  41:      
  42:         private void btnAbout_Click(object sender, System.EventArgs e)
  43:         {
  44:             NavigationService.Navigate(new Uri("/About.xaml", UriKind.Relative));
  45:         }
  46:  
  47:         private void btnSettings_Click(object sender, System.EventArgs e)
  48:         {
  49:             NavigationService.Navigate(new Uri("/RealmSettings.xaml", UriKind.Relative));
  50:         }
  51:  
  52:         private void btnRealm_Click(object sender, System.EventArgs e)
  53:         {
  54:             App.allRealms = false;
  55:             Browser.Navigate(new Uri(uriRealmsSingle, UriKind.Relative));
  56:           }
  57:  
  58:         private void btnAllRealms_Click(object sender, System.EventArgs e)
  59:         {
  60:             App.allRealms = true;
  61:             Browser.Navigate(new Uri(uriRealmsAll, UriKind.Relative));
  62:         }
  63:  
  64:         private void Browser_LoadCompleted(object sender, NavigationEventArgs e)
  65:         {
  66:             if (!App.allRealms)
  67:             {
  68:                 Browser.IsScriptEnabled = true;
  69:                 String[] realm = new String[1];
  70:                 realm[0] = HttpUtility.UrlEncode(App.userRealm);
  71:                 Browser.InvokeScript("getRealmStatus", realm[0]);
  72:  
  73:             }
  74:         }

You will notice that we are passing in the server name flag to the Web Browser Control itself through the InvokeScript method.  This allows us to generate user interaction in our native XAML based UI and then pass arguments to the JavaScript functions we load into the Web Browser Control.

Two things to point out here the first is that you must have scripting enabled on the control through the IsScriptEnabled flag and the second is that you’ll need to wait to call the JavaScript function once all the content has been loaded into the DOM.  I chose to do this through the control’s LoadCompleted Event.

Step 4. As you add more functionality to your own app you will want to include an About and Settings page.  Both of these are set up using native XAML controls.

The About page will hand of processing to the WebBrowserTask if a user decides to get more information.

XAML:

  1: <Grid x:Name="LayoutRoot" Background="Transparent">
  2:        <Grid.RowDefinitions>
  3:            <RowDefinition Height="Auto"/>
  4:            <RowDefinition Height="*"/>
  5:        </Grid.RowDefinitions>
  6:  
  7:        <!--TitlePanel contains the name of the application and page title-->
  8:        <StackPanel Grid.Row="0" Margin="12,17,0,28">
  9:            <TextBlock Text="{StaticResource app_title}" Style="{StaticResource PhoneTextNormalStyle}"/>
  10:            <TextBlock Text="about" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  11:        </StackPanel>
  12:        <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  13:            <TextBlock x:Name="txtAbout" HorizontalAlignment="Center" TextWrapping="Wrap" Text="The World of Warcraft Windows Phone 8 API Starter Kit is a completely free app for educational and entertainment purposes only. The code for this app is open source and available on Github. " VerticalAlignment="Top" Height="108" Width="446" />
  14:            <Button x:Name="btnGithub" Content="Download Source Code" Margin="0,40" Click="btnGithub_Click"/>
  15:            <TextBlock x:Name="txtAbout2" HorizontalAlignment="Center" TextWrapping="Wrap" Text="For more information aout my other free apps, developer starter kits, and online training courses check out my blog DaveDev.net." VerticalAlignment="Top" Height="108" Width="446" />
  16:            <Button x:Name="btnBlog" Content="Visit DaveDev.net" Margin="0,0,0,40" Click="btnBlog_Click"/>
  17:  
  18:        </StackPanel>

C#:

  1: private void btnGithub_Click(object sender, RoutedEventArgs e)
  2:        {
  3:            WebBrowserTask webBrowserTask = new WebBrowserTask();
  4:            webBrowserTask.Uri = new Uri("https://github.com/apimash", UriKind.Absolute);
  5:            webBrowserTask.Show();
  6:        }
  7:  
  8:        private void btnBlog_Click(object sender, RoutedEventArgs e)
  9:        {
  10:            WebBrowserTask webBrowserTask = new WebBrowserTask();
  11:            webBrowserTask.Uri = new Uri("https://davedev.net", UriKind.Absolute);
  12:            webBrowserTask.Show();
  13:        }

The Settings page will store any server name that a user enters and then use that for 

XAML:

  1: <Grid x:Name="LayoutRoot" Background="Transparent">
  2:         <Grid.RowDefinitions>
  3:             <RowDefinition Height="Auto"/>
  4:             <RowDefinition Height="*"/>
  5:         </Grid.RowDefinitions>
  6:  
  7:         <!--TitlePanel contains the name of the application and page title-->
  8:         <StackPanel Grid.Row="0" Margin="12,17,0,28">
  9:             <TextBlock Text="{StaticResource app_title}" Style="{StaticResource PhoneTextNormalStyle}"/>
  10:             <TextBlock Text="settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  11:         </StackPanel>
  12:  
  13:         <!--ContentPanel - place additional content here-->
  14:         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  15:             <StackPanel Margin="10,58,-10,339" Orientation="Vertical">
  16:                 <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="My Realm" Margin="10,0,0,0" FontSize="24"  />
  17:                 <TextBox x:Name="txtRealm" HorizontalAlignment="Left" Height="72"  TextWrapping="Wrap"  Width="406"/>
  18:  
  19:                 <Button x:Name="btnSave" Content="Save" HorizontalAlignment="Left" Width="152" Height="91" Click="btnSave_Click"  />
  20:             </StackPanel>
  21:         </Grid>
  22:     </Grid>

C#:

  1: public partial class RealmSettings : PhoneApplicationPage
  2:     {
  3:         public RealmSettings()
  4:         {
  5:             InitializeComponent();
  6:             InitUserSettings();
  7:         }
  8:  
  9:         private void InitUserSettings()
  10:         {
  11:             if (App.appSettings.Contains("realm"))
  12:             {
  13:                 txtRealm.Text = (string)App.appSettings["realm"];
  14:             }
  15:         }
  16:  
  17:         private void btnSave_Click(object sender, System.Windows.RoutedEventArgs e)
  18:         {
  19:             if (App.appSettings.Contains("realm"))
  20:             {
  21:                 App.appSettings["realm"] = txtRealm.Text;
  22:             }
  23:             else
  24:             {
  25:                 App.appSettings.Add("realm", txtRealm.Text);
  26:             }
  27:  
  28:             var result = MessageBox.Show("Your Realm has been updated to '" + txtRealm.Text + "'.", "Changes Saved", MessageBoxButton.OK);
  29:  
  30:             App.allRealms = false;
  31:             NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
  32:         }
  33:     }

Conclusion

Hopefully this Starter Kits will help aid you in your own Windows Phone app development. Be sure to check out the rest of my Starter Kits as well as the full APIMASH project here.

-Dave