Add contents of a stream to a Zip file

by CameronM 19. May 2016 04:05

In a recent project I needed to create a Zip file containing a number of files and a summary of the contents. Thanks to the .NET 4.5 ZipArcive class, this was relatively straigh forward.

One hurdle that needed to be overcome was adding the a text file to the Zip that contained a list of the files that had been Zipped along with some other information about each record. I started by creating a StreamWriter, using a MemoryStream as I didn't actually want to save the file to disk.

Lines containiing the file details would be written to the StreamWriter as each record was processed, then at the end of the process, the MemoryStream would be saved to the ZipArchive.

// create and save zip file
using (var outStream = new MemoryStream())
{
    using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
    {
    MemoryStream ms = new MemoryStream();
    var csv = new StreamWriter(ms);

    // call some process on each record sent into the function
    foreach (var graphic in Request.Items)
    {
        // write results to stream
        csv.WriteLine(...);
    }

    // add results to the zip
    var csvCompressed = archive.CreateEntry(Request.Record.BatchName + ".txt");
    using (var csvStream = csvCompressed.Open())
    {
        csv.Flush(); // write content to memory stream
        ms.Seek(0, SeekOrigin.Begin);
        ms.CopyTo(csvStream); // save memory stream to Zip
    }


    }
    // save the zip file
    using (var fileStream = new FileStream(fullPath, FileMode.Create))
    {
        outStream.Seek(0, SeekOrigin.Begin);
        outStream.CopyTo(fileStream);
    }
}

Tags:

C#

Get deals for games with the CheapShark API

by CameronM 7. August 2014 14:24

CheapShark provides a list of deals for games from many of the big suppliers, like Amazon. As part of the service, CheapShark provides an API that allows you to search for deals for a specific game.

In this post, we’ll look at how you can use the CheapShark API to build a Windows Phone 8 “Best-Price” app for games.

Creating our Visual Studio solution

Add a two new folders to the project, one named Models and the other ViewModels. Using nuget, either via the ‘Manage Packages’ context menu, or using the console, add the Microsoft HTTP Client Libraries and the Json.NET packages. These will help us call the web service and then convert the resulting Json.

Querying the CheapShark service

The Url for the CheapShark API is as follows:

http://www.cheapshark.com/api/1.0/games?title=[Search Term]

Using Json2CSharp, copy and paste the Json formatted results from a call to the above Url into the textbox, hit Generate get to resulting C# class. Add this into the Models folder as class named Result.

public class Result
{
    public string gameID { getset; }
    public string steamAppID { getset; }
    public string cheapest { getset; }
    public string cheapestDealID { getset; }
    public string external { getset; }
    public string thumb { getset; }
}

The MainViewModel contains an ObservableCollection of Result objects and a single asynchronous call to the CheapShark web service.

public class MainViewModel : NotifyBase
{
    string baseUrl = "http://www.cheapshark.com/api/1.0/";
 
    public MainViewModel()
    {
    }
 
    private ObservableCollection<Result> _Results;
    public ObservableCollection<Result> Results
    {
        get { return _Results; }
        set
        {
            _Results = value;
            OnPropertyChanged("Results");
        }
    }
 
    public async Task LoadDataAsync(string Title)
    {
        Uri uri = new Uri(string.Format("{0}games?title={1}", baseUrl, Title));
 
        await FetchDataAsync(uri);
    }
 
    private async Task FetchDataAsync(Uri uri)
    {
 
        HttpClient client = new HttpClient();
        try
        {
            var response = await client.GetStringAsync(uri);
 
            var results = JsonConvert.DeserializeObject<List<Result>>(response);
            this.Results = new ObservableCollection<Result>(results);
        }
        catch (System.Net.WebException exception)
        {
            string responseText;
 
            using (var reader = new System.IO.StreamReader(exception.Response.GetResponseStream()))
            {
                responseText = reader.ReadToEnd();
                throw new Exception(responseText);
            }
        }
    }
}

Creating the Windows Phone project

With the basics methods and properties in our PCL, it’s time to add a new Windows Phone App (found in the C#/Store Apps category in Visual Studio).

Add a reference to the CheapShark.PCL project so that we can use our MainViewModel and the nuget package for the Windows Phone Toolkit (just in case!).

Create an application property that exposes our MainViewModel, so that it can be referenced throughout the app. In the App.xaml.cs file add the following code.

private static MainViewModel _ViewModel;
public static MainViewModel ViewModel
{
    get 
    { 
        if(_ViewModel==null)
            _ViewModel = new MainViewModel();
        return _ViewModel;
    }
    set { _ViewModel = value; }
}

Calling the ViewModel from the MainPage

We’ll override the OnNavigatedTo event to link the MainPage with the MainViewModel. This ensure that the ViewModel is available whenever the page gets called. In the MainPage.cs file add the following code.

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    this.DataContext = App.ViewModel;
 
    await App.ViewModel.LoadDataAsync("Batman");
 
    base.OnNavigatedTo(e);
}

To start with, we’re going to hard code the search term we’re interested in, to confirm that everything is wired up correctly. 

Now that we’ve linked the ViewModel, it’s time to add the XAML that will display the results. Once again we'll use our derived LongListSelector control, so add a new xmlns named local to your MainPage.xaml as follows:

xmlns:local="clr-namespace:GameSearch.WP"    

Inside the ContentPanel Grid control, add the following mark-up:

<local:LongListSelector ItemsSource="{Binding Results}">
    <local:LongListSelector.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="0,0,0,12" >
                <Image Source="{Binding thumb}"
                       Width="100" 
                       VerticalAlignment="Top" 
                       HorizontalAlignment="Left"/>
                <StackPanel Margin="12,0,0,0" VerticalAlignment="Top" >
                    <TextBlock Text="{Binding external}" 
                                HorizontalAlignment="Left" 
                                VerticalAlignment="Top" 
                                FontSize="{StaticResource PhoneFontSizeLarge}" />
                    <TextBlock Text="{Binding cheapest}" 
                                HorizontalAlignment="Left" 
                                Style="{StaticResource PhoneTextSubtleStyle}" 
                                Margin="0,0,12,0" />
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </local:LongListSelector.ItemTemplate>
</local:LongListSelector>

Run the app on your device and test out the results – you should get a list of results that match your search term.

Tags: ,

C# | Windows Phone 8

GoodReads API

by CameronM 6. August 2014 13:57

GoodReads provides a way to discover books you might like based on your reading habits. You can create digital bookshelves containing books you have read or want to read. You can also search for books by title, which is the API function we’re going to tacking in today’s API August post.

Sigining up for a GoodReads API Key

Like the Google APIs, you need to sign up for an API key. Head to the api page for more information.

Creating our Visual Studio solution

Start by creating a new Class Library (Portable) project. Name the project GoodReads.PCL and the solution GoodReads.

Add a two new folders to the project, one named Models and the other ViewModels. Using nuget, either via the ‘Manage Packages’ context menu, or using the console, add the Microsoft HTTP Client Libraries.

Querying the GoodReads service

The Url for the GoodReads API is as follows:

http://www.goodreads.com/search/search?format=xml&key=[Your Key]&q=[Query] 

Unlike the Google Places API, GoodReads returns XML formatted results, so there are a couple of ways to create the C# classes to use for serialization. One option is to use the Paste XML as Class option from the Edit/Paste Special menu. Another is to use the xsd.exe tool, which is what I did when first investigating the GoodReads API some time ago.

The BookListViewModel contains an ObservableCollection of GoodreadsBook results and a single asynchronous call to the GoodReads web service.

public class BookListViewModel : NotifyBase
{
    string key = "[YourAPIKey]";
    string baseUrl = "http://www.goodreads.com/search/search?";
 
    public BookListViewModel()
    {
        this.Books = new ObservableCollection<GoodreadsBook>();
    }
    private ObservableCollection<GoodreadsBook> _books { getset; }
 
    public ObservableCollection<GoodreadsBook> Books
    {
        get { return _books; }
        set
        {
            _books = value;
            OnPropertyChanged("Books");
        }
    }
 
    public async Task LoadDataAsync(string Search)
    {
        try
        {
            Uri uri = new Uri(string.Format("{0}format=xml&key={1}&q={2}", baseUrl, key, Search));
 
            HttpClient client = new HttpClient();
            var response = await client.GetStringAsync(uri);
 
            StringReader sr = new StringReader(response);
 
            XmlSerializer xs = new XmlSerializer(typeof(GoodreadsResponse));
            GoodreadsResponse results = (GoodreadsResponse)xs.Deserialize(sr);
 
            // add each book from the works array
            for (int i = 0; i < results.Search.Results.Works.Length; i++)
            {
                this.Books.Add(results.Search.Results.Works[i].Books[0]);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

Creating the Windows Phone project

Add a new Windows Phone project to your solutions (it’s in the C#/Store Apps category in Visual Studio).

Add a reference to the GoodReads.PCL project so that we can use our ViewModel.

Create an application property that exposes our MainView model, so that it can be referenced throughout the app. In the App.xaml.cs file add the following code.

private static BookListViewModel _viewModel { getset; }
public static BookListViewModel ViewModel
{
    get
    {
        if (_viewModel == null)
            _viewModel = new BookListViewModel();
        return _viewModel;
    }
}

Calling the ViewModel from the MainPage

We’ll override the OnNavigatedTo event to link the MainPage with the BookListViewModel. This ensure that the ViewModel is available whenever the page gets called. In the MainPage.cs file add the following code.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    this.DataContext = App.ViewModel;
    base.OnNavigatedTo(e);
}

We’re using a simple textbox to collect the value to add to the search query so that users can enter the name of a book to search for. We’ve wired up the OnKeyDown event so that when the user taps the ‘enter’ key on the keyboard, the search is started.

private async void OnKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        _searchTerm = SearchTextBox.Text.Trim();
 
        if (String.IsNullOrEmpty(_searchTerm))
            return;
        this.Focus();
 
        // search the API
        await App.ViewModel.LoadDataAsync(_searchTerm);
 
    }
}

Now that we’ve linked the ViewModel, it’s time to add the XAML that will display the results. As before, we're using our derived LongListSelector control, so add a new xmlns named local to your MainPage.xaml as follows:

xmlns:local="clr-namespace:GoodReads.WP"

Replace the LayoutRoot Grid with the following mark-up:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
 
    <StackPanel Grid.Row="0" Margin="12,17,0,12" >
        <TextBox x:Name="SearchTextBox"
                    InputScope="Search" 
                    KeyDown="OnKeyDown"/>
    </StackPanel>
 
    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <local:LongListSelector x:Name="lstResults" 
                ItemsSource="{Binding Books}"  
                ItemTemplate="{StaticResource BookItemTemplate}" />
    </Grid>
</Grid>

One change you'll notice is that we're using a resource called BookItemTemplate rather than simply adding the markup to the ItemTemplate tag as we did in the Google Places API post. This maks it much cleaner to read the markup and also enables reuse of your resources, if you make them application wide. In this case, the resource has been added at the page level, rather than application, so you'll need to add the following to your page XAML.

<phone:PhoneApplicationPage.Resources>
    <DataTemplate x:Key="BookItemTemplate">
        <StackPanel Margin="12,12,0,12" Orientation="Horizontal">
            <Image Source="{Binding ThumbnailURL}"/>
            <StackPanel Margin="12,0,0,0">
                <TextBlock HorizontalAlignment="Left" 
                            Height="Auto" 
                            TextWrapping="Wrap" 
                            Text="{Binding Title}" 
                            VerticalAlignment="Top" 
                            Width="Auto" 
                            Style="{StaticResource PhoneTextAccentStyle}" 
                            Margin="0,0,0,0"/>
                <TextBlock HorizontalAlignment="Left" 
                            Height="Auto" 
                            TextWrapping="Wrap" 
                            Text="{Binding Author.Name}" 
                            VerticalAlignment="Top" 
                            Width="Auto" 
                            Margin="0,0,0,0"/>
            </StackPanel>
        </StackPanel>
    </DataTemplate>
</phone:PhoneApplicationPage.Resources>

Run the app on your device and perform a search for your favourite book. You should end up with a list of results that match your search term.


Tags: ,

C# | Windows Phone 8

Google Places API - Part 1

by CameronM 4. August 2014 08:54

Apps that help you find restaurants, bars, cinemas and other businesses are extremely popular. Apps like Yelp are bringing local search and reviews to the fore, with many people relying on these services to help them decide where to spend their hard-earned money.

Many of the big names, such as Google, also provide services that can be used to create your very own place-finding app. In this post, we’ll look at how you can user Google Places API to build a Windows Phone 8 ‘What’s near me’ app.

Signing up for the Google Places API

The first step is to sign up for the Google Places API. To do that, you’ll need a Google account and access to the API Console. Check out this page for more information about how to add access to the Place API to your account.

Once you’ve added the Places API to your account, you’ll need the take a note of the API key associated with that account to make any requests to the web service.

Creating our Visual Studio solution

Okay, so now that we have our API key, it’s time to fire up Visual Studio. Start by creating a new Class Library (Portable) project. Name the project GooglPlaces.PCL and the solution GooglePlaces.

Add two new folders to the project, one named Models and the other ViewModels. Using nuget, either via the ‘Manage Packages’ context menu, or using the console, add the Microsoft HTTP Client Libraries and the Json.NET packages. These will help us call the web service and then handle the resulting Json.

Performing a location based search

The Url for performing a basic Places search if as follows:

https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=[Latitude],[Longitude]&radius=500&key=[Your API Key]

One handy tool you can use when dealing with API’s that return Json, is found at Json2CSharp. Copy and paste the Json formatted results from a call to the above Url into the textbox, hit Generate and a bunch of C# classes will be displayed.  These classes have been added to the Models folder of our project. For readability, each class was added to its’ own .cs file, however you could simply add them as a single file also.

The MainViewModel contains an ObservableCollection of results and a couple of asynchronous methods that call the Places API. This will be enough to get us started with creating a Windows Phone app and displaying a list of results.

In our app we’ll be retrieving the Latitude and Longitude from the device, so these will need to be variables we send to the web service. We’ll use a hard coded radius and create a class variable to hold the API Key.

public class MainViewModel : NotifyBase
{
    string baseUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch";
    string apiKey = "[Your API Key]";
 
    private ObservableCollection<Result> _Results;
    public ObservableCollection<Result> Results
    {
        get { return _Results; }
        set
        {
            _Results = value;
            OnPropertyChanged("Results");
        }
    }
 
    public async Task LoadDataAsync(double Lat, double Long)
    {
        // convert the lat and long to a string
        string LatLong = string.Format("{0},{1}", Lat.ToString("0.0000"), Long.ToString("0.0000"));
 
        // build the NearbySearch Uri
        Uri uri = new Uri(string.Format("{0}/json?location={1}&radius=500&key={2}", baseUrl, LatLong, apiKey));
 
        // await the call to the API
        await FetchDataAsync(uri);
    }
 
    private async Task FetchDataAsync(Uri uri)
    {
        HttpClient client = new HttpClient();
        var response = await client.GetStringAsync(uri);
 
        var results = JsonConvert.DeserializeObject<ListRootObject>(response);
        this.Results = new ObservableCollection<Result>(results.results);
    }
}

Creating the Windows Phone project

With the basic methods and properties in our PCL, it’s time to add a new Windows Phone App (found in the C#/Store Apps category in Visual Studio).

Add a reference to the GooglePlaces.PCL project.

Create an application property that exposes our MainView model, so that it can be referenced throughout the app. In the App.xaml.cs file add the following code.

private static MainViewModel _ViewModel;
public static MainViewModel ViewModel
{
    get
    {
        if(_ViewModel == null)
            _ViewModel = new MainViewModel();
        return _ViewModel;
    }
    set
    {
        _ViewModel = value;
    }
 
}

Calling the ViewModel from the MainPage

We’ll override the OnNavigatedTo event to link the MainPage with the MainViewModel. This ensures that the ViewModel is available whenever the page gets called. In the MainPage.cs file add the following code.

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    this.DataContext = App.ViewModel;
 
    await App.ViewModel.LoadDataAsync(37.422, -122.083);
 
    base.OnNavigatedTo(e);
}

In this post just hard coding the latitude and longitude, to confirm that everything is wired up correctly. In a future post, we'll modify the code from How to get the phone's current location for Windows Phone 8 to get the actual location from the device.

Displaying the results

Now that we’ve linked the ViewModel, it’s time to add the XAML that will display the results. 

Add a new xmlns named local to your MainPage.xaml as follows:

xmlns:local="clr-namespace:GooglePlaces.WP"

Inside the ContentPanel Grid control, add the following mark-up:

<local:LongListSelector ItemsSource="{Binding Results}">
    <local:LongListSelector.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="12,0,0,17" >
                <Image Source="{Binding icon}" 
                        Height="48" Width="48" 
                        VerticalAlignment="Top" 
                        Margin="0,0,8,0"/>
                <StackPanel Margin="12,0,0,17" 
                            Width="432"
                            VerticalAlignment="Top" >
                    <TextBlock Text="{Binding name}" 
                                HorizontalAlignment="Left" 
                                VerticalAlignment="Top" 
                                FontSize="{StaticResource PhoneFontSizeLarge}" 
                                FontFamily="{StaticResource PhoneFontFamilyNormal}" />
                    <TextBlock Text="{Binding vicinity}" 
                                HorizontalAlignment="Left" 
                                Style="{StaticResource PhoneTextSubtleStyle}" />
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </local:LongListSelector.ItemTemplate>
</local:LongListSelector>

Run the app on your device and test out the results – you should get a list of places in the vicinity of the Lat/Long you hard-coded.

Tags: ,

C# | Windows Phone 8

API August

by CameronM 1. August 2014 12:28

Welcome to API August, where we’ll be looking at how to build simple Windows Phone 8 apps using some of the more common, freely accessible API’s and web services.

All of the projects we’ll be building during API August will use an MVVM pattern, so that all of the heavy lifting is carried out by a Portable Class Library (PCL). We’ll use a Windows Phone 8 project to test the PCL functionality, however, if you’re keen to develop cross-platform, there’s nothing to stop you using the PCL in a Xamarin Studio solution and creating iOS or Android app as well.

As we’ll be focussing on how to call these APIs and return the results to the user, we’ll be keeping the UI very simple. Mostly we’ll just present the results as a list of records with an accompanying page to show the details of a single record. Occasionally we’ll add some searching or filtering, but we’ll leave the rest to your imagination. Having covered the basics in these posts, you’ll be able to go out and build fantastic, full-featured apps that take the world by storm. If that does happen, be sure to drop me a line, or offer me a job:)

As API August continues, we’ll update this post with links to the projects we’re creating, so take the time to bookmark this page and check back regularly.

Here's some basics that will apply across all of the solutions we'll cover in API August.

Solution Layout

Most of our sample solutions will follow a similar pattern. We’ll have an solution with two projects SolutionName.PCL – the portable class library and SolutionName.WP – the Windows Phone 8 app.

Views - ViewModels

Since our Views need to know whenever the underlying ViewModel has changed, all of our ViewModels need to implement INotifyPropertyChanged. We’ll create a single base class NotifyBase that handles the PropertyChanged events, which our ViewModes will inherit.
public class NotifyBase : INotifyPropertyChanged
{
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected virtual void OnPropertyChanged([CallerMemberNamestring propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(thisnew PropertyChangedEventArgs(propertyName));
    }
 
}

LongListSelector with bindable SelectedItem

In the API August projects, we’ll use a class derived from the built-in LongListSelector to display lists of results. The derived class adds the implementation for the SelectedItem property. This is not supported in the built-in control, but is extremely handy. 
public class LongListSelector : Microsoft.Phone.Controls.LongListSelector
{
    public LongListSelector()
    {
        SelectionChanged += LongListSelector_SelectionChanged;
    }
 
    void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        SelectedItem = base.SelectedItem;
    }
 
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register(
            "SelectedItem",
            typeof(object),
            typeof(LongListSelector),
            new PropertyMetadata(null, OnSelectedItemChanged)
        );
 
    private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var selector = (LongListSelector)d;
        selector.SelectedItem = e.NewValue;
    }
 
    public new object SelectedItem
    {
        get { return GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }
}

Tags: ,

C# | Windows Phone 8

Windows Phone App Studio

by CameronM 24. June 2014 00:00

At a recent Microsoft event one of the presenters reminded the audience of the Windows Phone App Studio and mentioned a bunch of new features, including support for Windows 8.1 and Windows Phone 8.1 Universal Apps. 

I signed up for an account when App Studio was still in limited beta and was immediately impressed by the ease at which you could develop a simple content based app. I remember mentioning it to a couple of guys in the office and saying that if this sort of thing kept up, we'd all be out of jobs. 

Having not done a lot with App Studio since, I decided to fire it up again and see just how easy it was to create an app based on some publically available content and publish it to the store. 

I've got a friend who enjoys going on cruises, almost to the point of it being an obsession and as we'd all just booked cruises for later in the year, I decided I'd build an app for cruisers called CruiseJunkie. 

Step 1: Sign up for an account using your Microsoft account. 

Step 2: Start a new project and select the template type (I chose an Empty App) 

Step 3: Add some 'sections' to your app.  

Sections render as PanoramaItems on the main page of the app. There's a few common sections available, including Facebook, Instagram, and Flickr. You can also add your own collections, which could contain static data (stored in the app) or dynamic data (store in Azure). 

Step 4: Choose a style for your sections.  

You can choose from a large range of styles for each section so that no matter what sort of data you're pointing at, there's bound to be one that looks just right. 

Most sections consist of two pages, one showing a list of data and the other showing a single item. You can choose layouts for both of these pages.  

Step 5: Choose a theme for your app 

You can choose from a Light or Dark theme, or go ahead and create your own by setting Background images and choosing appropriate colours. 

Step 6: Upload your Tiles 

Getting artwork for you app, including the tiles, is probably the most time-consuming task in the whole process. I grabbed a suitable icon from the Noun Project for CruiseJunkie, so it didn't take too long. The App Studio page displays the sizes you need depending on the tile option you choose. For the Flip tile template, you'll need 160x160, 340x340 and 715X340 jpg images. 

Step 7: Download the source code, or publish you app 

Once you've completed the steps above, you're ready to start thinking about what to do next. You can download the source code and make additional changes using Visual Studio, or go ahead and publish the app. 

Since I wanted to test the end-to-end process I decided to publish the app. This process wasn't quite as easy as I had though it should be. App Studio includes a reasonably detailed set of instructions to follow here as well as a video

After you associate your Store app with your App Studio project you'll still need to download the complete package (XAP file and screenshots) and upload it into the correct places in the Store. I'd like to see this a little more integrated, since you've already provided most of the information needed by the Store in your project. Having said that, it is still really easy.

Previewing your app

One of the best features of App Studio is the live preview that lets you see exactly what your app looks like. The Preview is displayed on every step of the process, so you're never left wondering how the changes you are making will look in your app

Tags: , , ,

Windows Phone 7 | Windows Phone 8 | WP7 | WP8

Displaying the progress bar in WP8

by CameronM 4. February 2013 20:04

One of the really nice additions to the Windows Phone 8 SDK is the ProgressBar. This control, which displays the ‘loading’ dots in your app is such as common part of the Windows Phone OS that it was a surprise that in WP7 it wasn’t provided to developers out of the box.

This has changed in WP8 and showing the progress bar is a painless experience, but one that will immeasurably assist in the useability of you app.

The code below shows how you could display the progress bar while you wait for an asynchronous function to complete.

private async void btn_continue_Click(object sender, RoutedEventArgs e)
        {
            // show progress bar
            SystemTray.ProgressIndicator = new ProgressIndicator();
            SystemTray.ProgressIndicator.IsIndeterminate = true;
            SystemTray.ProgressIndicator.IsVisible = true;
 
            // call some long-running process
            await ...;
 
            SystemTray.ProgressIndicator.IsVisible = false;
 
        }

Tags: ,

Windows Phone 8 | WP8

Asset Management #2 - Selecting a location

by CameronM 12. January 2013 11:56

In this post we’ll continue building an Asset Management app that enables a field worker to selected a point on the map and lodge a new defect.

In the first post we looked at how we can centre the map at the devices current location. In this post, we’ll add functionality to handle when the user selects a location on the map.

We want to do two things when the user selects a location. Firstly, we want to show a point on the map and secondly we want to store the street address. We’ve covered most of this code in previous posts, but here we’ll put it all together in a real-world scenario.

There are a number of ways to display a UI element on the Map control, including programmatically via code-behind and in XAML. In both cases, we’ll use the Pushpin class that comes with the Windows Phone Toolkit.

Adding a Pushpin Programmatically

To add a pushpin to the Map control programmatically we’ll need to declare a couple of local variables for a Pushpin, MapOverlay and MapLayer. We’ll create a new map overlay that is displayed on a map layer when the page first loads, then we’ll display a Pushpin to it when the user taps the map. As we only want one Pushpin, we’ll reset the location of the Pushpin every time the user taps the maps, rather than creating a new Pushpin.

// variables for displaying the pushpin
Pushpin pin = new Pushpin();
MapOverlay overlay = new MapOverlay();
MapLayer layer = new MapLayer();
 
// local variable to store the selected location
MapLocation defectLocation;

The next step is to add a handler for the Tap event for the Map control. This handler needs to place a Pushpin on the Map and then call the ReverseGeocodeQuery to get the street address.

<maps:Map x:Name="LocationMap" Tap="LocationMap_Tap"  />
private void LocationMap_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    //get the location of the Tap
    GeoCoordinate location = LocationMap.ConvertViewportPointToGeoCoordinate(e.GetPosition(LocationMap));
 
    //show a pushpin on the map
    pin.GeoCoordinate = location;
 
    //set the overlay location
    overlay.GeoCoordinate = location;
 
    //add the pushpin to the overlay
    overlay.Content = pin;
 
    //reverse geocode the location
    ReverseGeocodeQuery reverse = new ReverseGeocodeQuery();
    reverse.GeoCoordinate = location;
    reverse.QueryCompleted += reverse_QueryCompleted;
    reverse.QueryAsync();
}

When the ReverseGeocodeQuery completes, we’ll store the address and suburb details in a local variable. This value, along with the location of the Pushpin could then be saved along with any other relevant details, to create a new asset defect.

void reverse_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
    // store the address in a local variable
    if(e.Error==null)
    {
        //grab the first location
        if (e.Result.Count() > 0)
        {
            defectLocation = e.Result.FirstOrDefault();
        }        
    }
}

Modify the OnNavigatedTo event to include code to add our overlay to the layer and the layer to the map.

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    // get current location
    Geolocator locator = new Geolocator();
    Geoposition position = await locator.GetGeopositionAsync();
    var location = position.Coordinate.ToGeoCoordinate();
 
    // center the map at the current location
    LocationMap.Center = location;
    LocationMap.ZoomLevel = 18;
 
    //the map overlay will hold the pushpin
    overlay.PositionOrigin = new Point(0, 1);
 
    //add the overlay to the layer 
    layer.Add(overlay);
 
    //add the layer to the map
    LocationMap.Layers.Add(layer);
 
    base.OnNavigatedTo(e);
}

The code is pretty self-explanitory, except perhaps why we're setting the overlay.PositionOrigin. If you comment out this line, you'll notice that the Pushpin displays a little below the location you clicked on the map. Setting the origin of the overlay rectifies this.

Test the app and when you tap on the app, the pushpin will be displayed. Tap again in a different location and the pushpin will be moved to that location.

Tags: , ,

Windows Phone 8 | WP8

Asset Management #1 - Getting users location

by CameronM 10. January 2013 17:38

While building a recent asset management app for mobile field staff, I had to create a page where they could lodge defects. Part of the lodgement process required them to select the location of the defect (latitude/longitude) and enter the physical street address.

The original HTML5 app that I built used the javascript version of Nokia HERE maps that enabled the field staff to perform these tasks, but moving to Windows Phone 8 meant that this could now be handled by the new API features.

The first step to achieve this is to add a Map control to your XAML page, remembering to include the Microsoft.Phone.Maps.Control XML namespace in the page declaration.

xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
<maps:Map x:Name="LocationMap" />

When the user opened the page, I wanted the map to zoom to their current location to make it as quick as possible to select the asset that needed fixing. To do this, we need to initialise a Geolocator, which can be used to return the current location.

using Windows.Devices.Geolocation;

Override the OnNavigatedTo to get the current location and center the map

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    // get current location
    Geolocator locator = new Geolocator();
    Geoposition position = await locator.GetGeopositionAsync();
    var location = position.Coordinate.ToGeoCoordinate();
 
    // center the map at the current location
    LocationMap.Center = location;
    LocationMap.ZoomLevel = 18;
 
    base.OnNavigatedTo(e);
}

One catch with this process is that the classes used to represent a location from the Geolocator and the Map are similar, but actually different. The Geolocator returns a Windows.Devices.Geolocation.Geocoordinate objects, but to use this on a map we need a System.Device.Location.GeoCoordinate.

While it’s possible to write your own converter, the Windows Phone Toolkit provides a handy map extension that does the conversion for us. You’ll need to add a reference to the Toolkit in your code-behind.

using Microsoft.Phone.Maps.Toolkit;

Then you can call the ToGeoCoordinate() extension method of the Geocoordinate class.

Before testing your app, be sure to set the ID_CAP_LOCATION capability for you app in the WMAppManifest or you'll get an exception.

Tags: , ,

Windows Phone 8 | WP8

Windows Phone 8 - The new map services

by CameronM 10. December 2012 17:11

One of the really nice enhancements that have been made to the mapping capabilities in the Windows Phone 8 SDK is the addition of services such as geocoding and reverse geocoding, which are functions that are often required when building map-based solutions.

I wrote a post some time ago showing how you could utilise the Bing Maps API to perform these tasks, but in WP8, they’re built in to the SDK. GeocodeQuery and ReverseGeocodeQuery are two classes in the new Microsoft.Phone.Maps.Services namespace that provide these function. 

GeocodeQuery

Geocoding is the process of retrieving a geographical location (latitude/longitude) when provided a physical address, such as a street and suburb name. You’d use geocoding in your app if you want users to enter an address in a textbox and then see that displayed on a map.

The code to perform a GeocodeQuery couldn't be much easier. Assuming you have a TextBox name SearchTextBox where the user will enter the address to query and a Button that they press, your code to perform the query will look as simple as this.

private void SearchButton_Click(object sender, RoutedEventArgs e)
{
    GeocodeQuery query = new GeocodeQuery();
    query.SearchTerm = SearchTextBox.Text;
    query.GeoCoordinate = new System.Device.Location.GeoCoordinate(-27.5, 153);
    query.QueryCompleted += query_QueryCompleted;
    query.QueryAsync();
}

You will need to decide how to handle the results of the query, which if successful will be a list of MapLocation objects. For this example we'll just display the street address of the first record, but obviously you could display the results in a list and let the user select the relevant record.

void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
    //check that the query didn't raise an exception
    if(e.Error==null)
    {
        //grab the first location//
        if (e.Result.Count() > 0)
        {
            var location = e.Result.FirstOrDefault();
            MessageBox.Show(location.Information.Address.Street);
        }
    }
}

The MapLocation class contains a number of properties, including GeoCoordinate (latitide/longitude) and Information. The Information property returns a LocationInformation object that can be used to retrieve values such as the Address, Description and Name of the location. Depending on the addres that was entered, not all of these values will be populated. For example, if the user enters a regular suburban address, only the Address property is populated.

ReverseGeocodeQuery

As the name suggests, reverse geocoding is the process of retrieving a physical address (street/suburb) when provided a geographical location (latitude/longitude). You’d use reverse-geocoding in your app if you wanted users to be able to tap a point on a map and see what the address is.

To illustrate the code to perform a ReverseGeocodeQuery, we’ll need a simple UI that allows a user to select a location on a map. You can either add a map via code or in XAML, but we’ll use XAML for this example.

Firstly add the XML namespace to your page declaration. 

xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"

Add a map control and set an appropriate Center and ZoomLevel and add the Tap event handler. 

<maps:Map x:Name="LocationMap" Center="-27.5, 153" ZoomLevel="10" Tap="LocationMap_Tap"  />

Next we’ll need to handle the Tap event, which will be triggered when the user taps the map. 

private void LocationMap_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    //get the location of the Tap
    GeoCoordinate location = LocationMap.ConvertViewportPointToGeoCoordinate(e.GetPosition(LocationMap));
 
    //reverse geocode the location
    ReverseGeocodeQuery reverse = new ReverseGeocodeQuery();
    reverse.GeoCoordinate = location;
    reverse.QueryCompleted += reverse_QueryCompleted;
    reverse.QueryAsync();
}

Lastly, you'll want to handle the callback from when the query completes. This could be as simple or as complex as you like, but for this example we'll just show the street name of the location.

void reverse_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
    // do something with the address
    if(e.Error==null)
    {
        //grab the first location
        if (e.Result.Count() > 0)
        {
            var location = e.Result.FirstOrDefault();
            MessageBox.Show(location.Information.Address.Street);
        }        
    }
}

Tags: , ,

Windows Phone 8 | WP8