Visually Located

XAML and GIS

Working with the MapView control in the ArcGIS Runtime

This is the third in a series of posts covering the new ArcGIS Runtime SDK. In part 2, I discussed the basic mapping layers within the ArcGIS Runtime. Now that we know how to add a map and layers to your app, let’s see what we can do with the map! As I mentioned in the first post, the new ArcGIS Runtime has a MapView, rather than a Map that you put in your app. The MapView has a Map property that must be set, but all of the functionality lies in the MapView. We’ll start out with some of the key dependency properties. We’ll then look at the map events, and finish with the few methods.

Dependncy Properties

The new MapView control has 11 dependency properties (with one additional attached dependency property). In this blog we’ll focus on seven of them. Not to worry about the other four. One we have already covered (Map), one will be covered in the next blog and two more when discussing GIS focused work. In this blog I’ll cover the following dependency properties.

MapGrid

The MapGrid is a new addition from the older .NET SDKs. I must say that this one is really cool. This allows you to display a map grid on the map. If you want to build an app with map, and not a GIS focused app, you may be wondering why this is useful. You could create your own MapGrid and have them be borders in a map. Maybe you are creating a multi-player game with a map. Each player has different zones they are responsible for.

The SDK comes with four map grids. The LatLonMapGrid shows latitude and, longitude on the map. The MgrsMapGrid displays the Military Grid Reference System. The UsngMapGrid shows the United States National Grid reference system. Finally, the UtmMapGrid shows the Universal Transverse Mercator coordinate system.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:ArcGISTiledMapServiceLayer ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer"/>
        </esri:Map>
    </esri:MapView.Map>
    <esri:MapView.MapGrid>
        <esri:LatLonMapGrid/>
    </esri:MapView.MapGrid>
</esri:MapView>
MaximumExtent

The MaximumExtent property allows you to control how far out the map should be allowed to zoom. This property only controls zoom, it does not allow you to restrict where the user can pan the map. This property must be set using the spatial reference that the map uses. It would be great if you could specify an extent using lat/lon but have a map with a coordinate system of Web Mercator. The following example sets the maximum extent of the map to be the lat/lon borders of Colorado.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:ArcGISTiledMapServiceLayer ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
        </esri:Map>
    </esri:MapView.Map>
    <esri:MapView.MaximumExtent>
        <geom:Envelope YMax="41.0014594" YMin="37.9358477" XMin="-111.09373110000001" XMax="-101.893834999999970000">
            <geom:Envelope.SpatialReference>
                <geom:SpatialReference Wkid="4326"/>
            </geom:Envelope.SpatialReference>
        </geom:Envelope>
    </esri:MapView.MaximumExtent>
</esri:MapView>
Min and MaxScale

The MinScale and the MaxScale properties control how far out or in, respectively, the map is allowed to zoom. A MaxScale of 1,000 will allow the map to be zoomed in to about a neighborhood level. A value of 10,000 no closer than a town, and 100,000 about the Denver metro area. The MinScale works the same way, but allowing the map to be zoomed out. When using the MinScale, ensure the map has an initial extent set so the user is not lost in the ocean.

From the documentation

A scale is usually referred to as 1:X, where X is the scale specified here. This value is the relative scale to the real world, where on inch on the screen is X inches in the real world. Note that this is only an approximation and is dependent on the map's projection that can add some distortion, as well as the system's reported DPI setting which doesn't necessarily match the actual DPI of the screen.

Rotation

The Rotation property allows you to… you guessed it, rotate the map. This is really useful when you want to orientate the map with the compass on the device or allow for a manual rotation of the map using an orientation control within the app.

ShowMagnifierOnTapAndHold

ShowMagnifierOnTapAndHold allows a magnifier to appear when you tap and hold the map. This property only works when using a finger to tap a touch device. I’ve been told the intent of this property is to better aid editing work flows. This property could be used for much more and make a mapping experience a lot better.

WrapAround

This is a handy property that allows to specify if the map can be continually panned east or west and have the map continue. Think of the WrapAround property as the ability a globe to spin continuously.

Events

The MapView control has four events that you’ll want to familiarize yourself with. The ExtextChanged event notifies you when the map is zoomed or panned. The MapView has three other events for touch or click manipulation. These events are MapViewDoubleTapped, MapViewHolding, and MapViewTapped.

Methods

The MapView has two utility methods and methods to zoom and center the map. Use the LocationToScreen and ScreenToLocation methods to convert between map points to screen points. These methods are handy within events. The MapView four methods for zooming and 15 methods for centering the map! Three methods are synchronous and 12 are asynchronous. To be fair, there are two methods, SetView  with two overloads and SetViewAsync with 11 overloads. Working with map APIs, you usually have one, at most two ways of centering a map. Oddly enough, the MapView does not have a MapCenter dependency property to center the map with binding.

I’ll continue the next post discussing location within an ArcGIS Runtime map app.

Working with Basic Mapping Layers in the ArcGIS Runtime Map

This is a second in a series of posts covering the new ArcGIS Runtime (beta). In the last post we walked through creating a simple map app. In this post we’ll start to cover the different layers your map can have. A Layer can be map images like what you would see in Google or Bing Maps. Or a Layer can represent the physical location of items. Layers can be toggled on and off through the IsVisible property. You can also adjust the transparency of a layer with the Opacity property.

The ArcGIS Runtime SDK comes with 12 different types of map layers that you can use in your app! Some of these I’ll cover in detail in this blog, some I’ll just barely cover, and some will have whole blogs dedicated to them. I like to group the layers into two basic categories. The layers for “basic mapping” and layers for “GIS users”. I consider basic mapping to be any app in the store that uses a map in any way. Some examples would be Four Square, or Disney Expedition.  Layers for GIS users are needed for the types of applications that I write for Schneider Electric. Applications that have a heavy workflow around GIS data or using Esri services need “GIS Layers”. Most of the apps that you write should be fine with the Basic Mapping layers.

This blog post will cover the Basic Mapping layers. Another post will cover the GIS User layers

Basic Mapping

ArcGISTiledMapServiceLayer

Of all the “ArcGIS” layers, this is the only one that makes it into the Basic Mapping section. All of the layers prefixed with ArcGIS work specifically with ArcGIS Server services. They know what URIs to go to for map images for an area or other information about the content of the layers. The ArcGISTiledMapServiceLayer makes it into the Basic Mapping category because Esri offers many great up-to-date tiled maps for free. That’s right, you can use any of the MapServer services located at http://services.arcgisonline.com/ArcGIS/rest/services. If we ignore the folders (which contain even more services), Esri offers 12 tiled services.

image_thumb

All of these services use either a standard WGS84 projection or a Web Mercator projection.

UPDATE: I was told that all of the WGS84 services have been deprecated in favor of the Web Mercator services. So you should not use the ESRI_Imagery_World_2D service. Instead use the World_Imagery service.

Using the ArcGISTilesMapServiceLayer in your app is really simple. Click on a link for one of the services that you would like to use in your app. I will pick “World_Street_Map”. Copy the URL of the service from your browser. For me it will be http://services.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer. This URL will be the ServiceUri for the layer. The following XAML will allow you to use the Layer in your map app.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:ArcGISTiledMapServiceLayer 
                   ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer"/>
        </esri:Map>
    </esri:MapView.Map>
</esri:MapView>

It’s that easy to start using free maps with the ArcGIS Runtime SDK and the ArcGISTiledMapSerivceLayer!

image

BingLayer

The ArcGIS Runtime comes with out-of-the-box support for Bing map images! The BingLayer requires a key from the Bing maps portal. When working with the BingLayer, you can specify if you want an aerial, aerial with labels, or a road map through the MapStyle property. The default map style is Road. I won’t get into a lot of detail about the Bing maps portal, but keep in mind that you will have to pay for the usage of the map data if requests are over 50,000 in 24 hours. To use the BingLayer in your app, use the following XAML.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:BingLayer Key="YOUR-BING-KEY" MapStyle="AerialWithLabels"/>
        </esri:Map>
    </esri:MapView.Map>
</esri:MapView>

Which produces the following map

image

GraphicsLayer

The GraphicsLayer is used to show any “client side” data. Think of it as the place for your map pins. The GraphicsLayer has changed from older versions of the .NET SDKs. In the past you were able to style a Graphic any way you wanted in XAML. The new API does all of it’s rendering in C++. With the rendering that deep, it can’t use XAML. They do provide some nice out of the box ways to draw your symbols. The GraphicsLayer is basically like a ItemsControl. You add items to the Graphics collection much like you would the Items of an ItemsControl. You can also bind the GraphicsSource to an IEnumerable<Graphic> just like you would the ItemsSource of an ItemsControl. Unlike an ItemsControl, the GraphicsLayer cannot accept any object into it’s collection. You can only add Graphic objects.

For this sample we need two layers in our Map. One for a base map and our GraphicsLayer. We’ll add new Graphics to the map whenever you tap or click the map. We’ll also define what we want our “map pin” to look like in the page resources. You can specify how each individual Graphic should look, or define a Render for the GraphicsLayer. For this we’ll specify that every Graphic should render an ‘X’ on the map.

<Page.Resources>
    <symbols:SimpleMarkerSymbol x:Key="GraphicSymbol" Color="#FF0A57FC" Size="12" Style="X" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <esri:MapView MapViewTapped="OnMapViewTapped">
        <esri:MapView.Map>
            <esri:Map>
                <layers:ArcGISTiledMapServiceLayer ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
                <layers:GraphicsLayer>
                    <layers:GraphicsLayer.Renderer>
                        <symbols:SimpleRenderer Symbol="{StaticResource GraphicSymbol}"></symbols:SimpleRenderer>
                    </layers:GraphicsLayer.Renderer>
                </layers:GraphicsLayer>
            </esri:Map>
        </esri:MapView.Map>
    </esri:MapView>
</Grid>

In our code behind we’ll add new graphics to the map in the OnMapViewTapped method.

private void OnMapViewTapped(object sender, MapViewInputEventArgs e)
{
    var mapView = (MapView)sender;
    var graphicsLayer = (GraphicsLayer)mapView.Map.Layers[1];
    graphicsLayer.Graphics.Add(new Graphic(e.Location));
}

The GraphicsLayer requires a lot more information than this overview blog so I have two more blogs that are dedicated to this topic.

image_thumb1

CsvLayer

The CsvLayer is a new addition to the ArcGIS Runtime. This handy layer allows you to use a csv file as the source of data for a map. Suppose you have a file with all of the locations of Starbucks. The CsvLayer allows you to use this file to show locations on a map! This file can be located online, or it can be local file. As of writing this, XAML only allows for an online file through the ServiceUri property. If you have a local file with your app, or would like to allow the user to specify a file, you can use the SetSourceAsyc method. The CsvLayer is a type of GraphicsLayer. So you can use the Graphics or GraphicsSource properties directly, but you shouldn’t need to. Just like with the GraphicsLayer, you define what the Graphics should look like with the Render property.

<Page.Resources>
    <symbols:SimpleMarkerSymbol x:Key="GraphicSymbol" Color="#FF0A57FC" Size="12" Style="X" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <esri:MapView >
        <esri:MapView.Map>
            <esri:Map>
                <layers:ArcGISTiledMapServiceLayer 
                        ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
                <layers:CsvLayer ServiceUri="http://VisuallyLocated.com/samples/starbucks_sample.csv">
                    <layers:CsvLayer.Renderer>
                        <symbols:SimpleRenderer Symbol="{StaticResource GraphicSymbol}"></symbols:SimpleRenderer>
                    </layers:CsvLayer.Renderer>
                </layers:CsvLayer>
            </esri:Map>
        </esri:MapView.Map>
    </esri:MapView>
</Grid>

In the above XAML, I’m have a file with a few locations of Starbucks around the US. Here is what it looks like.

image_thumb2

OpenStreetMapLayer

The OpenStreetMapLayer is also a new addition to the SDK. As you can guess, it displays the Open Street Map data. This is one of those things that “just works”.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:OpenStreetMapLayer />
        </esri:Map>
    </esri:MapView.Map>
</esri:MapView>

Here is the app showing Open Street Map data for Colorado.

image_thumb3

You can download a solution demonstrating all five of these layers in a Windows Store and Windows Phone app.

Read part 2: Working with the MapView control in the ArcGIS Runtime

Building a map app with the ArcGIS Runtime Map

This is the first in a series of posts that will go over the new ArcGIS Runtime SDK. This first post will discuss how to get started and create an app that has the ArcGIS Runtime Map. This first step is to download the SDK. The SDK is currently in beta so you will need to sign up for the beta program. It’s important to note that you will not be able to get the SDK from nuget.

Esri packages their installers a little differently than you might be used it. The exe that you download is a zip of the actual installer. Run the ArcGIS_Runtime_SDK_for_DotNet_1022_536.exe (as of writing this the file is missing the exe extension, so you’ll need to add it) and you will be prompted for a location to unpackage the installer.

image

Give it a location, once it completes it will run the actual Setup.exe.

image

image

We’ll be building a Windows Store app with the SDK so you’ll need Visual Studio 2013. The SDK does not support Windows 8 apps, only Windows 8.1 apps. For Windows Phone and WPF you can use Visual Studio 2012. Open up Visual Studio and create a new Windows Store project.

image

The core of the ArcGIS Runtime is built with C++. Because of this, you’ll need to change the platform of the application from Any CPU. Any CPU is a .NET only thing, so we’ll need to specify either x86, x64 or ARM. To change the platform right click on the solution and select Configuration Manager. You may also want to add the platform target dropdown to a toolbar..

image

Make sure that debug and release are set to something other than Any CPU. If you like using the designer, you need to pick x86. When you ship your app, you’ll package it up with x86, x64 and ARMso it will work on any Windows 8.1 device.

image

Next add a reference to the new ArcGIS Runtime assembly. The assembly is under the Windows –> Extensions group.

image

Open the XAML of your MainPage. For this example we’ll create a fully emersive mapping application. We’ll need to add two namespaces to our xaml.

<Page
    x:Class="App1.MainPage"
    ...
    xmlns:esri="using:Esri.ArcGISRuntime.Controls"
    xmlns:layers="using:Esri.ArcGISRuntime.Layers">

If you are using Windows Phone, make sure you define the namespaces properly

<phone:PhoneApplicationPage
    xmlns:esri="clr-namespace:Esri.ArcGISRuntime.Controls;assembly=Esri.ArcGISRuntime"
    xmlns:layers="clr-namespace:Esri.ArcGISRuntime.Layers;assembly=Esri.ArcGISRuntime">

The esri namespace is where the controls live. This is where we will find the new MapView control.  In the past, we have worked with Map control. Now the Map is a property of the MapView. This does seem odd as they have only pulled out the InitialExtent property and the Layers property. The rest of the map functionality still resides on the MapView.

The layers namespace is where we will find the different types of layers we can  to the map. The next post will have more information on the different layers. For this post we’ll use the ArcGISTilesMapServiceLayer using Esri’s free tiled map services.

With the name spaces defined, add the following XAML your page.

<esri:MapView>
    <esri:MapView.Map>
        <esri:Map>
            <layers:ArcGISTiledMapServiceLayer 
                    ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/ESRI_Imagery_World_2D/MapServer"/>
        </esri:Map>
    </esri:MapView.Map>
</esri:MapView>

Run the application and you’ll have a map app! Pan, zoom around. Find your home, have fun!

image

As you can see it is pretty simple to get started with the ArcGIS Runtime SDK. In the next post I’ll cover the different layers in the SDK.

Notice the legal notice in the corner? We are currently using the developer license (or no license) on the SDK. The SDK is still in beta, so you cannot use it in a production app (without asking). When the SDK is final, you can use a basic license for free. This license will get you pretty much everything you need to build an app. I’ll walk through registering for a license when it’s all working properly


You can download this solution that also contains a Windows Phone 8 app as well!

Read Part 2: Working with Basic Mapping Layers in the ArcGIS Runtime 

Getting started with the ArcGIS Runtime (beta) Mapping SDK for .NET

Last Sunday (March 9) Esri announced the public beta release of their mapping SDK for .NET. This new SDK covers Windows Store, Windows Phone, and WPF. Can you believe it? All three platforms, and all using the same API. This is huge news if you develop mapping applications. Personally I find it painful that in Windows Phone 7 you would use the Bing Maps (Silverlight) SDK. In Windows Phone 8 you were encouraged to use the new Nokia Maps SDK, while on Windows Store you had the Bing Maps SDK, but it was a different SDK than the Windows Phone 7 one. So that’s three different SDKs to use. I have no idea who thought this was a sane idea.

Esri has been working hard on this new SDK. During the Devsummit last year (2014), Esri announced the beta of a new SDK that would target Windows Store. At that time, they already had a Windows Phone SDK and a WPF SDK. Both were very similar with a few extras on the WPF side. While working on this new beta for Windows Store, they decided it would be much better to have a new SDK that targeted Phone, Store, and WPF. So for the past year that’s what they have been doing.

Over the coming weeks I’ll be blogging about how you can create mapping apps that use this new SDK. Some of these blogs will have a heavy focus on the GIS person, while others will be targeted to everyday app builders who want a better experience building map apps. These blogs will focus on Windows Store, but can be applied to Windows Phone and WPF as well.

 

Part 1: Building a map app with the ArcGIS Runtime Map
Part 2: Working with Basic Mapping Layers in the ArcGIS Runtime Map
Part 3: Working with the MapView control in the ArcGIS Runtime 
Park 4: Working with location in an ArcGIS Runtime Map app
Part 5: Adding map pins (graphics) to the ArcGIS Runtime Map
Part 6: Customizing map pins (graphics) in the ArcGIS Runtime Map

Part x: Working with the “GIS User” Layers in the ArcGIS Runtime
… More to come.

Extending your app theme into the SystemTray

I have seen a lot of apps recently in which the developers think about the theme of their app. They have nice colors, either in text, or background or both.

Sidebar: I recently read in an MSDN blog that “Backgrounds are discouraged. They are allowed for brand reasons and to support contrast. Instead, use any accent colors on the text foreground”. I personally like when apps do this rather than just the plain white or black text/background offered by default. This makes the app stand out from the rest. Heck, even Microsoft has violated this rule with their bing apps. I have a dark theme for my phone, yet the bing apps seem to favor a white background.

One area I notice where people are not applying their theme is in the system tray. They spend a lot of time and effort theming their pages, yet neglect this area. This is actually not surprising as the system tray is easy to overlook when creating your apps. It is placed in by default, it’s very small (32 pixels), and when using the emulator it’s hard to distinguish the tray from the emulator. Forgetting to theme this part of your app results in the following.

theme

Notice the black bar at the top?

How can you fix this? There are a couple ways to add your theme to the system tray.

  • Change the BackgroundColor of the SystemTray
  • Change the Opactiy of the SystemTray
Change the BackgroundColor of the SystemTray

The simplest way to extend your theme into the system tray is to change the BackgroundColor.

<phone:PhoneApplicationPage
    ...
    shell:SystemTray.IsVisible="True"
    shell:SystemTray.BackgroundColor="DarkGreen">

Let’s take a look at what happens to your app style when changing the color.

theme2

Notice anything off? Let me zoom in a little.

image

Notice the black line between the tray and the page content? This happens if you set the background through the PhoneApplicationFrame, or through the page content. If you are setting the background from the frame there is no way to remove the black line. If you set the background color from the page content, then you can remove the black line by shifting your page content up by one pixel.

<phone:PhoneApplicationPage
...
shell:SystemTray.IsVisible="True"
shell:SystemTray.BackgroundColor="DarkGreen">
 
<Grid x:Name="LayoutRoot" Background="DarkGreen" Margin="0,-1,0,0">
Unfortunately this means that you have to set the background for every page, rather than once in the frame.
Change the opacity of the SystemTray

A second option is to change the Opacity of the SystemTray. Opacity behaves very differently for the phone shell controls (eg: SystemTray, ApplicationBar) than it does for XAML controls. In XAML, opacity defines if an item should have some transparency (ie: Change the alpha channel). In Shell controls, opacity also defines the transparency but it is quite different. Setting a value less than one does not cause the content of the tray to become opaque. Instead it changes opacity of the background color.

Changing the opacity also allows controls to move into the space of the tray. Think of opacity on shell items like a grid with two rows.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="32"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
</Grid>

When opacity is set to “1” (the default), the tray and the page content fill only one row each.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="32"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <SystemTray Grid.Row="0"/>
    <PhoneApplicationPage Grid.Row="1"/>
</Grid>

When opacity is less than one, the page content is allowed to be in both rows.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="32"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <SystemTray Grid.Row="0"/>
    <PhoneApplicationPage Grid.RowSpan="2"/>
</Grid>

This causes your entire page to shift up! Shifting your content up means that your page is not quite aligned anymore.

image

To fix this, we’ll need to take into account whether the background for the page was set through the PhoneApplicationFrame, or through the page content. With either approach, we will add a top margin of 32. Which approach is used determines where we place the margin.

If you set the background of the PhoneApplicationFrame, you can safely add a margin to the first child of your pages. The background of the frame will fill the entire phone screen while your page content is shifted down slightly.

<Grid x:Name="LayoutRoot" Margin="0,32,0,0">
    <!-- Title-->
    <!-- Content -->
</Grid>

If you set the background of your page, you will want to add another child control to your LayoutRoot grid.

<Grid x:Name="LayoutRoot" Background="DarkGreen">
    <Grid Margin="0,32,0,0">
        <!-- Title-->
        <!-- Content -->
    </Grid>
</Grid>

If your pages have a Panorama with a background set you’ll want to modify the style to ensure that title does overlap the tray. The following image is using the default Panorama App template. The only change was to set visible to true, and opacity to 0.

image

The app has the exact same look without needing to add any margins. This gives the user the benefit of the system tray, without taking space from your app!

Using this approach how do you have a different color for the tray area than you do for the page content? You can accomplishing this by placing grid inside your page that has the background set. With this approach, you’ll need to take into account the margin.

<Grid x:Name="LayoutRoot" Margin="0,32,0,0" >
    <Grid Background="LightGreen" Height="32" VerticalAlignment="Top" Margin="0,-32,0,0"/>
    <!-- Title-->
    <!-- Content -->
</Grid>

image

Conclusion

So which approach do I recommend? Changing the BackgroundColor or the Opacity of the tray? Sorry, but going to answer “it depends”. It depends on how you want your page transitions to function, It depends on whether you want a different color for your tray than for your pages. Pick the solution that works best for you. For either solution I highly recommend setting the background of the PhoneApplicationFrame rather than the background of your page content. Setting the background of the frame allows page transitions to flow without showing the phone background (white or black) between pages.

I do recommend that you show the system tray, especially if your app uses an internet connection. If your app is loading slow, users will wonder if they have a good connection. They will want to tap the tray to see if they have a good cell signal or are connected to WiFi. If you hide the tray, the user has no idea if they are connected without exiting your app. You can always shift up your content closer to the tray in order to show more content. You can also place your app title in the system tray.

NOTE: All of this also applies to the ApplicationBar!

Pluralsight subscription give away

Congratulations to Mohana Rao Ramasetty and Mils†ein® for winning a free one month subscription to pluralsight!


I have two one month subscriptions to Pluralsight that I will give away to two lucky people at the end of the week. These are two one month subscriptions that I have won, but will not use as I already have an account. I have confirmed with Pluralsight that the codes are valid.

image

All you have to do is tweet

I want a free one month subscription to @pluralsight from @skendrot

Rules:

  1. Tweet the above
  2. You do not need to follow me. In fact, you can unfollow me if you so choose.
  3. More tweets != more chances to win.
  4. Contest ends midnight 1 Mar EST.

Add a persistent element to every page of your phone apps

Adding ad controls to apps is a pretty common thing. Some apps have ads on every page of the app. Some apps only have ads on some pages. When you want to have ads on one page you have to manually add the control to each page. Wouldn’t it be cool to only add it once and it shows up on all of your pages? This is exactly what someone recently asked on Stack Overflow. This is possible to do and quite simple. First, add a new style to your App.xaml resources and name it AdPhoneApplicationFrameStyle.

<Style x:Key="AdPhoneApplicationFrameStyle" TargetType="toolkit:TransitionFrame">
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeNormal}"/>
    <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="BorderBrush" Value="{x:Null}"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="phone:PhoneApplicationFrame">
                <Border x:Name="ClientArea" BorderBrush="{TemplateBinding BorderBrush}" 
                        BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" 
                        HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" 
                                          Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <Rectangle Height="80" Width="480" Fill="Red" Grid.Row="1"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

In the style I’ve added a new Rectangle to simulate our AdControl. The Rectangle has been placed inside a Grid in the second row which is oriented to the bottom. Next in the App.xaml.cs within the InitializePhoneApplication method, set the style on RootFrame object.

private void InitializePhoneApplication()
{   
    ...
 
    RootFrame = new TransitionFrame();
    RootFrame.Style = (Style)Resources["AdPhoneApplicationFrameStyle"];
 
    ...
}

Now you always have that element for every page of your app!

That’s pretty cool, but what about doing page transitions? Hopefully you are putting page transitions into your app. This is still possible, but we need to change the style of the Frame to match the Frame being used. If you are using the Windows Phone Toolkit TransitionFrame, your style would change to the following.

<Style x:Key="AdTransitionFrameStyle" TargetType="toolkit:TransitionFrame">
    <Setter Property="Background" Value="{StaticResource PhoneBackgroundBrush}"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="toolkit:TransitionFrame">
                <Border x:Name="ClientArea" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                        Background="{TemplateBinding Background}" Margin="{TemplateBinding Margin}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <ContentPresenter x:Name="FirstContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <ContentPresenter x:Name="SecondContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <Rectangle Height="80" Width="480" Fill="Red" Grid.Row="1"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

This style uses the same approach to add the Rectangle by adding it to a second row in the grid. When using the WP Toolkit TransitionFrame, you need to change the code within the InitializePhoneApplication method to create a TransitionFrame rather than a PhoneApplicationFrame.

private void InitializePhoneApplication()
{
    ...
 
    RootFrame = new TransitionFrame();
    RootFrame.Style = (Style)Resources["AdTransitionFrameStyle"];
 
    ...
}

Now we have our persistent element and our page navigation; the best of both worlds!

Asynchronous Predicates, Actions and Funcs

I love the built in Predicate, Action, and Func delegates. They provide simple ways for methods to be passed to other objects. Suppose you had an interface that processes some results. In the Process method, it has a parameter that specifies if a given item is valid and ready for processing

public interface IProcessResults
{
    IEnumerable<IProcessedItem> Process(IEnumerable<IItem> items, Predicate<IItem> isValid = null);
}

If the predicate existed, the implementing class would call that method to determine if an item is valid or not. This is all well and good, but we live in async world now. Windows Store APIs enforce async everywhere they can. Windows Phone and the .Net APIs are not quite there, but do provide everything needed so that you may be able to make your app Fast and Fluid. Why should we be held back by these synchronous delegate methods?

Let’s see if we can take the existing APIs, and make them asynchronous. First we have to define our predicate.

Predicate<IItem> shouldProcess;

Maybe we want to prompt the user with a MessageDialog asking them if they want to process the item.

Predicate<IItem> shouldProcess = item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

We cannot call await on the ShowAsync method, because our Predicate is not marked async. We can attempt to make our method async, by adding the keyword.

Predicate<IItem> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

But then we get the error

The return type of an async method must be void, Task, or Task<T>

Using Resharper, I can hit Alt-Enter and change the Predicate to Func<IItem, Task<bool>>

Func<IItem, Task<bool>> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

Goodness, that looks horrible! I wanted a simple Predicate and I ended up with Func<IItem, Task<bool>>. Our interface would need to change the method signature to the following:

public interface IProcessResults
{
    Task<IEnumerable<IProcessedItem>> Process(IEnumerable<IItem> items, Func<IItem, Task<bool>> shouldProcess = null);
}

We can make this better by creating our own delegate.

public delegate Task<bool> AsyncPredicate<in T>(T obj);

I choose to go with AsyncPredicate rather than PredicateAsync because PredicateAsync sounds weird. With this change, we can modify our interface as such

public interface IProcessResults
{
    Task<IEnumerable<IProcessedItem>> Process(IEnumerable<IItem> items, AsyncPredicate<IItem> shouldProcess = null);
}
And the creation of our predicate turns into
AsyncPredicate<IItem> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

In my mind this looks and reads much better. Now that we have one delegate, we can start creating more, and more.

public delegate Task<bool> AsyncPredicate<in T>(T obj);
public delegate Task AsyncAction();

Place your app title in the System Tray

I’ve been using the new Bing apps a lot and I really like how the app name is in the SystemTray.

wp_ss_20140211_0001

This looks awesome and frees up valuable space for your app! I have found that sometimes I remove the app title from pages to allow for more content. Accomplishing this for your apps is soooo simple! All you need to do is comment out the TextBlock that you are using for your title, and add the following bit of xaml to your page (outside of any grids and such)

<shell:SystemTray.ProgressIndicator>
    <shell:ProgressIndicator Text="My Application" IsVisible="True" IsIndeterminate="False"/>
</shell:SystemTray.ProgressIndicator>

Make sure you have set the SystemTray to be visible by setting the IsVisible property to true. You still get the advantage of the text going away when the user taps the tray.

If you do this, you will need to rethink how you show that the app is loading. The ProgressIndicator is suppose to be used to show that stuff is happening and that the user should keep calm, and wait. You have a few options for showing loading status.

Continue to use the SystemTray ProgressIndicator

You can continue to use the ProgreessIndicator within the SystemTray if you so wish. You will need to change the IsIndeterminate property and probably the Text Property.

// Make sure to give the ProgressIndicator a name
Indicator.Text = "Loading";
Indicator.IsIndeterminate = true;

The nice thing about the ProgressIndicator is that the properties are DependencyProperties. This means that they accept binding and can be set from a ViewModel.

<shell:SystemTray.ProgressIndicator>
    <shell:ProgressIndicator Text="{Binding IndicatorText}" IsVisible="True" IsIndeterminate="{Binding IsLoading}"/>
</shell:SystemTray.ProgressIndicator>

With either approach make sure that you set the Text and IsIndeterminate back when loading is done.

Use the ProgressBar

The ProgressBar is a very handy control already built into the ecosystem. It is very similar to the the ProgressIndicator in the SystemTray. The ProgressBar is an actual Control, this means you set the Visibility property instead of the IsVisible property. If you are binding, you will need a BooleanToVisibilityValueConverter. It still has an IsIndeterminate property to specify if you should see the dots flying across the screen or a solid line showing how much progress has been made. This is what the Bing apps use. When using this control in apps, make sure not to allocate space for it. What I mean by this is put it in a Grid in the same row as your page title, or other content. If you specify a row for it, or put it in a StackPanel, your page content will jump when the ProbressBar is shown or hidden.

Here is a xaml snippet of it being used.

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    
    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel Grid.Row="0" Margin="12,17,0,28">
        <!--<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>-->
        <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
 
    <ProgressBar Grid.Row="0" VerticalAlignment="Top" Foreground="White" 
                 IsIndeterminate="{Binding Loading}" 
                 Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisConverter}}" />

Notice the progress dots below.

progressbar

Use a third party control such as Telerik’s RadBusyIndicator

The RadBusyIndicator from Telerik is a nice control that has many default style options. This control allows you to restyle to whatever you want. I use this control for my main loading screen in Disney Expedition. Much like the IsVisible property of the ProgressIndicator, the RadBusyIndicator has an IsRunning boolean property to specify if it should be visible or not. You can change between 9 preconfigured animations by setting the AnimationStyle property.

<telerikPrimitives:RadBusyIndicator IsRunning="True" AnimationStyle="AnimationStyle1" Grid.RowSpan="2" Background="CC000000"/>

This control will take up as much space as it is given and have a background that covers all of the content on your page. If you want to create your own style, you can set the IndicatorAnimationStyle.

 

Putting your app title in the SystemTray really helps you show more content in your apps and let’s you continue to brand your app with your title.

Crop and resize any image to create a lockscreen for your phone with the [RTM] Nokia Imaging SDK

Awhile back I blogged about this same topic but with the beta version of the Nokia Imaging SDK. When Nokia released the RTM of their Imaging SDK, this functionality changed. We’ll take a look at how to accomplish this same functionality with the new Imaging SDK.The good news we will be able to reuse the GetRandomImage method that picked a photo from the phones library.

private Picture GetRandomImage()
{
    var rand = new Random(DateTime.Now.Millisecond);
 
    MediaLibrary library = new MediaLibrary();
    var album = library.RootPictureAlbum;
 
    int albumIndex = rand.Next(0, album.Albums.Count - 1);
    album = album.Albums[albumIndex];
 
    var pictureIndex = rand.Next(0, album.Pictures.Count - 1);
    var picture = album.Pictures[pictureIndex];
 
    return picture;
}

We will also be able to reuse the GetCropArea method without any changes. This method decides where we need to crop the given picture so it will be the same dimensions as the phone. This method may return a size larger or smaller than the phone itself, but it will be the same ratio of width/height. The ratio is the important part as we can shrink or grow the image with the Imaging SDK.

/// <summary>
/// Returns the area needed to crop an image to the desired height and width.
/// </summary>
/// <param name="imageSize">The size of the image.</param>
/// <param name="desiredSize">The desired size to crop the image to.</param>
/// <returns></returns>
private static Rect? GetCropArea(Size imageSize, Size desiredSize)
{
    // how big is the picture compared to the phone?
    var widthRatio = desiredSize.Width / imageSize.Width;
    var heightRatio = desiredSize.Height / imageSize.Height;
 
    // the ratio is the same, no need to crop it
    if (widthRatio == heightRatio) return null;
 
    double cropWidth;
    double cropheight;
    if (widthRatio < heightRatio)
    {
        cropheight = imageSize.Height;
        cropWidth = desiredSize.Width / heightRatio;
    }
    else
    {
        cropheight = desiredSize.Height / widthRatio;
        cropWidth = imageSize.Width;
    }
 
    int left = (int)(imageSize.Width - cropWidth) / 2;
    int top = (int)(imageSize.Height - cropheight) / 2;
 
    var rect = new Windows.Foundation.Rect(left, top, cropWidth, cropheight);
    return rect;
}

Because we made the CropPicture method in a nice generic way, we will be able to reuse the signature of that method as well. We will just need to modify the implementation of it to use the new SDK. This is one of the best parts of coding to a generic platform or contracts rather than to the SDK. When the SDK breaks it’s API, your code doesn’t have to.

The logic for this method is still the same, but Nokia completely changed the classes involved. In the beta of the Imaging SDK, you worked with an EditingSession, giving the session a Stream to work with. You could create filters from the FilterFactory or just create your own implementation of IFilter.

With the new SDK, you work with an IImageProvider. There are many out of the box image providers. For our method, we need to work with a StreamImageSource. This class gives you the ability to apply filters to a Stream. To apply filters you create a FilterEffect class, and pass in the image provider.

IImageProvider imageProvider = new StreamImageSource(stream)
IFilterEffect effect = new FilterEffect(imageProvider);

Filters can be applied by setting the Filters property with a collection of filters you want to apply. For our case we only want to use the CropFilter.

Windows.Foundation.Rect? rect = GetCropArea(new Windows.Foundation.Size(picture.Width, picture.Height), desiredSize);
if (rect.HasValue)
{
    var filters = new List<IFilter>();
    filters.Add(new CropFilter(rect.Value));
    effect.Filters = filters;
}

When you want to save a new image with filters applied, you create a new IImageConsumer. For our case, we want to save a jpeg image so we’ll use the JpegRenderer. To ensure that the image is the correct size of the phone, you need to set the Size property.

JpegRenderer renderer = new JpegRenderer(effect);
 
// We went through a lot of trouble to crop this at the proper ratio
renderer.OutputOption = OutputOption.PreserveAspectRatio;
renderer.Size = desiredSize;

And when you are ready to make the new picture, simply call the RenderAsync method of the renderer.

IBuffer buffer = await renderer.RenderAsync();

Putting all of the pieces together for our method we now have our code to crop and resize any image to fit the dimensions of the phone.

/// <summary>
/// Crops a Picture to the desired size.
/// </summary>
/// <param name="picture">The Picture to crop.</param>
/// <returns>A copy of the Picture which is cropped.</returns>
public static async Task<IBuffer> CropPicture(Picture picture, Windows.Foundation.Size desiredSize)
{
    using (var stream = picture.GetImage())
    {
        using (var imageProvider = new StreamImageSource(stream))
        {
            IFilterEffect effect = new FilterEffect(imageProvider);
 
            // Get the crop area of the image, we need to ensure that
            // the image does not get skewed
            Windows.Foundation.Rect? rect = GetCropArea(new Windows.Foundation.Size(picture.Width, picture.Height), desiredSize);
            if (rect.HasValue)
            {
                var filters = new List<IFilter>();
                // Define the effects to apply
                filters.Add(new CropFilter(rect.Value));
                effect.Filters = filters;
            }
 
            using (var renderer = new JpegRenderer(effect))
            {
                renderer.OutputOption = OutputOption.PreserveAspectRatio;
                renderer.Size = desiredSize;
 
                IBuffer buffer = await renderer.RenderAsync();
 
                return buffer;
            }
        }
    }
}

And as before, we can use the GetRandomLockscreen method that was created before.

private async Task<Stream> GetRandomLockscreen()
{
    // Get the width and height of the phone
    double phoneWidth = Resolution.PhoneWidth;
    double phoneHeight = Resolution.PhoneHeight;
 
    Picture mediaPicture  = GetRandomImage();
    IBuffer croppedImage = await CropPicture(mediaPicture, new Size(phoneWidth, phoneHeight));
 
    if (croppedImage == null) return null;
 
    return croppedImage.AsStream();
}

I was really hoping the new SDK would work better in background agents, but it will still use up the 10 megs of memory that are allowed to background agents. If you have not heard, with GDR3 background agents for low memory phones has been increased to 11 megs and non-low memory phones have been increased to 20 MB. This is pretty exciting news if you are creating lockscreens from in a background agent. Just remember that low memory phones are still low memory phones.