Day #31: Geocoding and More Fun In Bing Maps for Silverlight

Can you believe it? We’re at the end of this (longer than it should have been) journey. Today is the last day of the 31 Days of Silverlight. We are going to be building a more advanced version of the application we started yesterday, Day #30, related to Bing Maps in Silverlight.

1. Geocoding an address

For those of you relatively new to mapping, there’s this wonderful concept called Geocoding which makes our lives as developers much, much simpler. The basic premise is that we can provide our application with an actual address, and have a geocoding service figure out the latitude/longitude coordinates of that address. This way, we don’t have to tell our users to figure it out for themselves. Can you imagine going to Bing Maps to look at your house, only to be told you need to know the lat/long of that location? Not exactly user friendly. So, let’s get to it.

2. Adding the Geocoding service to our application

Before we can geocode an address, we need to add a reference to Bing’s geocoding web service. So let’s start there. Click on your Silverlight project, and choose “Add service reference…” Use this address as your address, and click Go:

http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc

You should end up with a window that looks like this:

3. Creating an asynchronous call to that service

Much like talking to any web service, we have a couple of things we need to do. The first is to create a new instance of the service. The second is to attach a Completed event handler to the service, so that we know when a response has occurred. Finally, we need to create our service request, and call the service asynchronously. In my example, I created a method called Geocode that takes the same two parameters as the web service: an address, and a waypointIndex. I’ll explain waypointIndex later, when we talk about routing and directions. So, here’s what my method looks like:

        private void Geocode(string address, int waypointIndex)
{
GeocodingService.GeocodeServiceClient geocodingService = new GeocodingService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
geocodingService.GeocodeCompleted += new EventHandler<SilverlightBingMapsGeocoding.GeocodingService.GeocodeCompletedEventArgs>(geocodingService_GeocodeCompleted);

GeocodingService.GeocodeRequest request = new GeocodingService.GeocodeRequest();
request.Credentials = new Credentials();
request.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)myMap.CredentialsProvider).ApplicationId;
request.Query = address;

geocodingService.GeocodeAsync(request, waypointIndex);
}

You can see that we also have to attach our API Credentials that we got in Day #30 to our request. Without a valid API key, we can’t make requests. Since I stored mine in the XAML, the reference you see in my code above is to that XAML location. For the Completed event handler, we need the event method for that as well. In mine, I am capturing the new Location object it returns, and adding that as a pushpin to my map, like we did yesterday. This solution only works when you’re certain the service will only be returning one value to you. Here’s the code:

        void geocodingService_GeocodeCompleted(object sender, SilverlightBingMapsGeocoding.GeocodingService.GeocodeCompletedEventArgs e)
{
Location center = e.Result.Results[0].Locations[0];
myMap.Center = center;
Pushpin pushpin = new Pushpin();
pushpin.Location = center;
myMap.Children.Add(pushpin);
}

After all of that is done, we just need to call our Geocode() method. In my example, I’m using the address of my office again. Here’s the method call:

Geocode("8800 Lyra Avenue, Columbus, OH  43240", 1);

4. Geocoding’s easy. How about turn-by-turn?

Directions are a little trickier, but not significantly. In fact, we wouldn’t need to change our Geocode method one bit. The major changes we make to our application include using another web service, the RouteService, and adding the Waypoints that it returns to your map.

There’s an excellent explanation of this on the MSDN reference for Bing Maps in Silverlight. You can read it here. http://msdn.microsoft.com/en-us/library/ee681887.aspx

I was originally planning on writing out an entire tutorial for this section as well, but after reading their explanation, I realized I’d only be taking away from an excellent article.

Summary

Getting an address to a latitude and longitude point is nothing to sneeze at. I don’t even want to think about what is required to happen inside those services. I’m happy to have this nice, thick layer of abstraction to manipulate. I think you’ll find as you go forward that the route plotting is really just as simple.

To download the source of this Silverlight Geocoding for Bing Maps application, click here.

This series on Silverlight has certainly been quite an adventure. I hope that you had an opportunity to learn a few things along the way. I certainly know that I did.

This coming February 2010, I am going to be starting another series, with a slightly less focused approach. They will all be related to software development and technology, but I’m going to branch out a bit. My first two posts will cover creating a theme in Windows 7, and the second will cover XNA development for the Zune. I also have plans to cover topics like ASP.NET MVC 2, among others. If there’s something you’d like to see me cover in this as-to-be-named series, please let me know, and I’ll do my best to accomodate you. For now, however, I’m signing off of this 31 Days of Silverlight, and look forward to the next adventure.

P.S. Tune in tomorrow for a game I created in Silverlight. The first 5 people to finish it will win a free O’Reilly ebook of their choice!

Day #30: Bing Maps In Silverlight

Today we continue our exploration through the 31 Days of Silverlight with something new. We now have the ability to add a Bing Maps control to our Silverlight application. There’s some downloading and installation necessary, so let’s start there.

1. Get the Bing Maps Silverlight Control SDK

It sounds as simple as it is. You can download and install the Bing Maps Silverlight Control SDK here. Once you’ve installed it, jump to the next step.

2. Get a Bing Maps API key

Next, you need to head over to http://www.bingmapsportal.com. Log in with your Live ID, and create a key. You’ll need this to build your application, so even though you’re thinking about skipping this step, don’t. Go get it.

3. Create a new Silverlight project

This is Day #30 of my Silverlight series, so I’m going to assume at this point that you know how to do this.

4. Add the Map .dll references

When you installed the SDK, it dropped two DLLs on your hard drive. If you left the default path during installation, you should be able to find them here: C:Program FilesBing Maps Silverlight ControlV1Libraries. We’re almost ready to add a map to our page.

5. Add the namespace declaration

The final “prep” step we need to take is to add the XML namespace to the top of our XAML file. We have done this in previous projects, so I’ll just show you what you need to add:

xmlns:bing="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

6. Adding a map to your page

To take it very simply, we can add one line of XAML to get a map on our page. In fact, here’s what it looks like:

<bing:Map CredentialsProvider="put your api key here" />

And here’s what the control looks like (see this in a new window):

7. Customize away

There’s an absolutely crazy amount of stuff you can do to customize these maps. I’m going to show you a couple of the simple ones, saving some cooler stuff for the next post. Specifically, how to have it start on a specific location, and how to add points to a map.

8. Defining a starting position

The simple way to do this is just to add a latitude/longitude value directly to at map’s Center property. You can do this via the codebehind, or directly in your XAML. Like this:

themap.Center = new Location(40.1449, -82.9754);
themap.ZoomLevel = 15;

OR

<bing:Map Name="themap" Center="40.1449, -82.9754" ZoomLevel="15" CredentialsProvider="your api key"  />

You’ll notice that I also set the ZoomLevel in both examples, so that we were zoomed in near the location we centered on. That’s pretty simple, but it doesn’t show the user the location we were trying to call their attention to. Next, we’ll actually put a pushpin on the map.

9. Adding a pushpin to the map

There’s actually a Pushpin class, which is convenient, especially when we need to have collections of pushpins. I have named my map “themap” for this example. Also, you’ll notice that I have moved my latitude/longitude values into a seperate Location object. This way, I can reference that location multiple times, without having to continue to hardcode the coordinates.

What you will see in the following code is that after setting the center of my map (and its zoom level), I create a new Pushpin, set its location, and then add it as a Child to my map. Here’s the code:

            Location myOffice = new Location(40.1449, -82.9754);
themap.Center = myOffice;
themap.ZoomLevel = 15;
Pushpin myOfficePin = new Pushpin();
myOfficePin.Location = myOffice;
themap.Children.Add(myOfficePin);

And here’s how it looks on the map (see this in a new window):

To download this Bing Maps Silverlight solution, click here.

Summary

Tomorrow, on our final day of this series, we’ll be diving deeper into this mapping control, geocoding (turning an address into latitude/longitude), and directions from one point to another. See you then!

Day #29: Using Isolated Storage in Silverlight

Today is day #29 in my 31 Days of Silverlight series. We’re going to cover how to utilize the isolated storage available on the user’s machine today. We can use isolated storage when we are in out-of-browser mode, and I recently dedicated 3 entire posts (Days #23 – #25) to getting your application ready for out-of-browser. We will be starting with that codebase, and you can download my Silverlight Out of Browser application here.

1. Adding some using statements

First things first, we’re going to need some way to write files, and the way we do that is with the System.IO namespace family. We’re going to add two different using statements to our MainPage.xaml.cs file:

using System.IO;
using System.IO.IsolatedStorage;

These allow us to read and write files, as well as access the Isolated Storage on the user’s machine.

2. Adding to our XAML file

For this tutorial, I am going to be adding a textbox to our page that will contain the lyrics to the song “Big Bang Theory,” by the Barenaked Ladies. We’re making this box editable, however, so that a user can write in their own lyrics, or fix typos they may find. They will be saving these changes locally to their isolated storage. Here’s what our new XAML file looks like…the only real additions are a TextBox and a Button (for saving):

<UserControl x:Class="SilverlightOutOfBrowser.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<
Canvas x:Name="LayoutRoot">
<
Canvas.Background>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FF2A2A2A" Offset="1"/>
<
GradientStop Color="#FF9D9D9D"/>
</
LinearGradientBrush>
</
Canvas.Background>
<
Image x:Name="networkimage" Height="48" Width="48" Canvas.Left="532" Canvas.Top="10" Source="network_unknown.png" Stretch="Fill" ToolTipService.ToolTip="Network Connectivity"/>
<
MediaElement x:Name="videobox" Height="384" Width="512" Source="/BigBangTheory.wmv" Stretch="Fill" Canvas.Left="12" Canvas.Top="10"/>
<
Image x:Name="outofbrowserimage" Height="48" Width="48" Canvas.Left="585" Canvas.Top="10" Source="outofbrowser_unknown.png" Stretch="Fill" ToolTipService.ToolTip="Install This Application"/>
<
TextBox x:Name="lyrics" Canvas.Top="400" Canvas.Left="12" Width="512" Height="80" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
<
Button x:Name="LyricSave" Click="LyricSave_Click" Width="100" Height="80" Content="Save Lyrics" Canvas.Top="400" Canvas.Left="530" />
</
Canvas>
</
UserControl>

3. Saving a file in isolated storage

Before we can retrieve our data from a file, we should probably write it, right? I am going to create a saveLyricsFile method that takes a string for the lyrics we want to save. All it really does is open a pathway to the isolated storage, creates a file (in our case, a .txt file), and then uses a StreamWriter to write the lyrics to the file. Here’s what my method looks like:

        string lyricsfile = "BigBangTheoryLyrics.txt";

private void saveLyricsFile(string lyricsToSave)
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = isf.OpenFile(lyricsfile, FileMode.Create, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(isfs))
{
sw.WriteLine(string.Format("{0}", lyricsToSave));
sw.Close();
}
}
}
getLyrics();
}

You’ll notice that I also call a new method called getLyrics() at the end of this method. Once I’ve saved the file to the user’s machine, I want a different method to retrieve the contents of that file, and write them to the TextBox on my page. That way, each time that the page loads, the lyrics box will have the most up-to-date version of the user’s lyrics file.

4. Retrieving the info in the file

Now that we have a file on the user’s machine, we need a way to get that information back, and use it. Using many of the same mechanisms from step #3, we are going to find our file in isolated storage, open it, and use a StreamReader to read the contents of the file into a string variable. Finally, we write that string to our TextBox. Here’s the method:

        string lyricstext = "Our whole universe was in a hot dense state,nThen nearly fourteen billion years ago expansion started. Wait...nThe Earth began to cool,nThe autotrophs began to drool,nNeanderthals developed tools,nWe built a wall (we built the pyramids),nMath, science, history, unraveling the mysteries,nThat all started with the big bang!nnSince the dawn of man is really not that long,nAs every galaxy was formed in less time than it takes to sing this song.nA fraction of a second and the elements were made.nThe bipeds stood up straight,nThe dinosaurs all met their fate,nThey tried to leap but they were latenAnd they all died (they froze their asses off)nThe oceans and pangeanSee ya, wouldn't wanna be yanSet in motion by the same big bang!nnIt all started with the big BANG!nnIt's expanding ever outward but one daynIt will cause the stars to go the other way,nCollapsing ever inward, we won't be here, it wont be hurtnOur best and brightest figure that it'll make an even bigger bang!nnAustralopithecus would really have been sick of usnDebating out while here they're catching deer (we're catching viruses)nReligion or astronomy, Encarta, DeuteronomynIt all started with the big bang!nnMusic and mythology, Einstein and astrologynIt all started with the big bang!nIt all started with the big BANG!";

private void getLyrics()
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isf.FileExists(lyricsfile))
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(lyricsfile, FileMode.Open, isf))
{
using (StreamReader sr = new StreamReader(isfs))
{
string lyricsdata = sr.ReadToEnd();
lyrics.Text = lyricsdata;
}
}
}
else
{
saveLyricsFile(lyricstext);
}
}
}

Notice that I set up an external string variable that is my original lyrics text. If the file doesn’t exist yet (a distinct possibility, since the user can delete it, or this may be their first time to the site), we need to populate it when we create it. So in my “else” statement, I call the saveLyricsFile method, passing those original lyrics in. Otherwise, if the file exists, we just read the contents of the file, and set those contents to the TextBox.Text value.

5. Calling the getLyrics() method when we load

We already have a Loaded event handler method, so let’s use that to call our getLyrics() method. This way, when the page loads, we’ll retrieve the contents of the file (and if the file doesn’t exist, we’ll create it.)

6. Making the button commit the “save”

The last step we need to take is to get our button to actually save the contents of the TextBox to the isolated storage file. The XAML I gave you earlier already had an event handler defined, so “navigate to event handler,” and add this line:

saveLyricsFile(lyrics.Text);

This will take the contents of our TextBox, and save it to the user’s local file. If you hit refresh in your browser, you should see that the new version of your text appears now. Because I embedded audio in my application, I am providing a link to see my Silverlight Isolated Storage application running. Normally I would just embed it here, but I don’t want to embarrass you at the office.🙂

You can download my solution for Silverlight Isolated Storage here.

7. Summary

Isolated Storage in Silverlight works both in and outside the browser, but there are different storage requirements depending on how you use it. Out of browser applications have access to 25 MB of storage, while in-brower applications only have 1 MB. The reason for this makes sense, though. When we’re running out of browser, we could be running disconnected, which would require us to store all of the user’s changes locally until we reconnect to the network.

In short, using isolated storage is not very different from writing to the file system in WPF, but we don’t get to specify paths on the user’s machine. All of our data and files reside inside a sandboxed area, that has no access to other parts of the user’s machine. This keeps them safe from malicious attacks that could occur under other circumstances. Here’s a look at where these files are stored:

Windows Vista/7
Users\AppDataLocalLowMicrosoftSilverlightis

Windows XP
Documents and Settings\Local SettingsApplication DataMicrosoftSilverlightis

Mac OS X
/Users//Library/Application Support/Microsoft/Silverlight/is

Only a few more days left in the 31 Days of Silverlight, and then a fun little Silverlight game for you on January 1st. I hope you are enjoying this series!

Day #28: Silverlight Application Themes

Welcome to Day #28. We’ve nearly finished the 31 Days of Silverlight, but we have a few fun posts remaining. Today’s post is on creating Themes in Silverlight.

Theming is one of those pieces of functionality you see in consumer apps quite a bit. Basically changing the application to look and feel the way I want it. However, it’s definitely more than that. It’s the way you should write your styling information in Silverlight. For the most part, it’s advanced Styling, which we covered on Day #10: Styling A Silverlight Control. The difference is that we are creating external style files that we can then swap between inside the application.

1. Download the Silverlight 3 Toolkit

This article will cover the advanced features that Theming offers you, and assumes that you are somewhat familiar with styling. First, you need to download the Silverlight 3 Toolkit. If you haven’t done this already, head over to http://silverlight.codeplex.com and download it.

2. Add references to two assemblies

We need to add two references to our Silverlight project. They are System.Windows.Controls, and System.Windows.Controls.Theming (this one was added by the Toolkit). Go ahead and add them to your project.

3. Add an XML namespace reference to the Theming assembly

The next thing, as usual, is to add an XML namespace reference. To do this, I recommend relying on Intellisense for it, and just start typing the xmlns:theming=” before it recommends a list of assemblies. If you don’t want to type it, however, you can just use my snippet of code:

xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"

4. Add some controls to your page

Before we can start styling our application, we need some controls on our page. I’ve added a TextBlock, a Button, a TextBox, and a Slider to my StackPanel base control. Here’s what my full XAML looks like right now:

<UserControl x:Class="SilverlightTheming.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<
StackPanel x:Name="LayoutRoot" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center">
<
TextBlock Text="This is our form." Margin="0,0,0,10"/>
<
Button Content="Save" Margin="0,0,0,10"/>
<
TextBox Text="Something to edit." Margin="0,0,0,10"/>
<
Slider/>
</
StackPanel>
</
UserControl>

5. Creating a ResourceDictionary

Our next step is going to be creating a ResourceDictionary file. If you choose “Add New Item…”, one of your choices is “Silverlight Resource Dictionary.” Even though it creates a XAML file (like nearly everything else in Silverlight), this will have a base tag of ResourceDictionary instead of UserControl. This is where we will define our theme. You’ll notice I named my file Green.xaml. This is so that I can remember that this is the green theme. Eventually, when you lather, rinse, and repeat, you’ll have Blue, Red, Black, White, Pink, etc. Effective naming is always useful. I never recommend going with names like “Professional,” “Glass,” and “Techno,” because it’s harder to remember what those look like just by reading the name.

One important note before you move on from this point: By default, your new resource dictionary will have a “Build Action” of “Page” in its properties. You need this to be “Content” instead. If you don’t change this, you will get an error similar to: AG_E_PARSER_BAD_PROPERTY_VALUE. Just change it. It will make your life easier than mine just was.

Editing our new ResourceDictionary

This is where the work comes in. For theming an entire application, we really need to consider the styles for every possible control we might use. In my example, I know I’m only going to be using 4 controls: TextBlock, TextBox, Button, and Slider. But as your application grows, you can see how this would grow quickly as well. (If you don’t want the theme to apply to everything, your life gets easier. This effort only applies to those controls you actually want to change visually.) To make my life easier, I have pre-defined some colors and brushes that I will be using in my theme. This way, if I ever want to change them in the future, I don’t have to change them in 19 different places. Here’s the entirety of my Blue.xaml file…don’t be intimidated. It’s big, but no worse than any of the stylesheets you’ve ever created.🙂

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!--DEFINING A LIST OF COLORS FOR RE-USE-->
<Color x:Key="TextBrush">#FF000000</Color>

<
Color x:Key="NormalBrushGradient1">#FFBAE4FF</Color>
<
Color x:Key="NormalBrushGradient2">#FF398FDF</Color>
<
Color x:Key="NormalBrushGradient3">#FF006DD4</Color>
<
Color x:Key="NormalBrushGradient4">#FF0A3E69</Color>

<
Color x:Key="NormalBorderBrushGradient1">#FFBBBBBB</Color>
<
Color x:Key="NormalBorderBrushGradient2">#FF737373</Color>
<
Color x:Key="NormalBorderBrushGradient3">#FF646464</Color>
<
Color x:Key="NormalBorderBrushGradient4">#FF000000</Color>

<
Color x:Key="ShadeBrushGradient1">#FF62676A</Color>
<
Color x:Key="ShadeBrushGradient2">#FFD1D4D6</Color>
<
Color x:Key="ShadeBrushGradient3">#FFFFFFFF</Color>

<
Color x:Key="SliderBorderGradient1">#FF3F3F3F</Color>
<
Color x:Key="SliderBorderGradient2">#FFADADAD</Color>

<!--DEFINING A LIST OF BRUSHES FOR RE-USE-->
<LinearGradientBrush x:Key="NormalBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="{StaticResource NormalBrushGradient1}" Offset="0" />
<
GradientStop Color="{StaticResource NormalBrushGradient2}" Offset="0.41800001263618469" />
<
GradientStop Color="{StaticResource NormalBrushGradient3}" Offset="0.418" />
<
GradientStop Color="{StaticResource NormalBrushGradient4}" Offset="1" />
</
LinearGradientBrush>

<
LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="{StaticResource NormalBorderBrushGradient1}" />
<
GradientStop Color="{StaticResource NormalBorderBrushGradient2}" Offset="0.38" />
<
GradientStop Color="{StaticResource NormalBorderBrushGradient3}" Offset="0.384" />
<
GradientStop Color="{StaticResource NormalBorderBrushGradient4}" Offset="1" />
</
LinearGradientBrush>

<
LinearGradientBrush x:Key="ShadeBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="{StaticResource ShadeBrushGradient2}" Offset="0" />
<
GradientStop Color="{StaticResource ShadeBrushGradient3}" Offset="0.1" />
<
GradientStop Color="{StaticResource ShadeBrushGradient3}" Offset="1" />
</
LinearGradientBrush>

<!--Button-->
<Style TargetType="Button">
<
Setter Property="Background" Value="{StaticResource NormalBrush}"/>
<
Setter Property="Foreground" Value="#FF000000"/>
<
Setter Property="Padding" Value="3"/>
<
Setter Property="BorderThickness" Value="2"/>
<
Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}" />
</
Style>

<!--TextBox-->
<Style TargetType="TextBox">
<
Setter Property="BorderThickness" Value="1"/>
<
Setter Property="Background" Value="{StaticResource NormalBrushGradient1}"/>
<
Setter Property="Foreground" Value="#FF000000"/>
<
Setter Property="Padding" Value="2"/>
<
Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}" />
</
Style>

<!--Slider-->
<Style TargetType="Slider">
<
Setter Property="BorderThickness" Value="1"/>
<
Setter Property="Maximum" Value="10"/>
<
Setter Property="Minimum" Value="0"/>
<
Setter Property="Value" Value="0"/>
<
Setter Property="BorderBrush">
<
Setter.Value>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFA3AEB9" Offset="0"/>
<
GradientStop Color="#FF8399A9" Offset="0.375"/>
<
GradientStop Color="#FF718597" Offset="0.375"/>
<
GradientStop Color="#FF617584" Offset="1"/>
</
LinearGradientBrush>
</
Setter.Value>
</
Setter>
</
Style>

</
ResourceDictionary>

6. Using the ImplicitStyleManager

That last step was the hard part. Defining styles for your controls, picking colors, and getting all of that XAML created. The ImplicitStyleManager makes the rest of our life easy. We can use it two different ways, and I’ll show you each. The first is the simple way: in XAML. To our “LayoutRoot” element, we just need to apply two properties:

<StackPanel Name="LayoutRoot" theming:ImplicitStyleManager.ApplyMode="Auto" theming:ImplicitStyleManager.ResourceDictionaryUri="Blue.xaml" HorizontalAlignment="Center" VerticalAlignment="Center"> 

Obviously, you need to make sure that where I put “Blue.xaml,” that you put the location of your ResourceDictionary. After that, run your application. If you used my style information, you should have slightly different looking button and slider controls, and a textbox that has a blue background. Each and every textbox in my application will look like that, unless I explicitly override that styling.

But Jeff, can I call this from code?

Why yes, you can! The other way to invote the ImplicitStyleManager is to just create a reference to it in code. Keep in mind that to do this, you’ll need a using statement for the System.Windows.Controls.Theming assembly, but otherwise, here’s the code:

            Uri theme = new Uri("Blue.xaml", UriKind.Relative);
ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, theme);
ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);
ImplicitStyleManager.Apply(LayoutRoot);

If you’re planning on giving your user the ability to change their theme on the fly, I’d recommend going the code route. You can add that code to an event handler, and just choose the appropriate Dictionary as the user selected it. So, that’s it! Pretty simple, huh?

Summary

I highly recommend Theming your application, regardless of whether you plan to allow your user to choose a theme or not. By externalizing all of your styling information, you create a familiar feel to traditional web development with CSS. In addition, you could technically create a seperate dictionary for Buttons, TextBlocks, etc. You’d then pull of those together in yet another master dictionary, keeping the code seperated and clean. Go forth and theme!

To download my application sample for Silverlight Theming, click here.

Day #27: Templating Controls In Silverlight

Ah, we’ve made it to Day #27 of the 31 Days of Silverlight. Today, we’re going to be decontructing one of the most common XAML elements: the Button. The reason we’re doing this is to demonstrate how templates work, and how even complex controls are really just compositions of much simpler ones.

1. Add a button to your XAML page

Button, button, who’s got the button? Just add a simple button to your page. If you need help, you can use this XAML:

<Button Content="Click Me!" Width="200" Height="200" />

2. Open your solution in Expression Blend 3

If you haven’t already installed Expression Blend 3, you can get a 60 day trial here.

Open MainPage.xaml, and you should see your button in the middle of the design surface. If you right click on the button, you should see a menu option: Edit Template. From there we’re going to choose “Create Empty…”

3. Creating a style resource

When you choose that menu item, you are presented with the “Create ControlTemplate Resource” dialog box. Here, you can name your new Template, as well as define its scope. For my example, I am choosing Application level scope, so that I can use this across all of my pages. What you will see is that you have added a big style-type definition to your App.xaml file. (For more info on styling, you can check out Day #10: Styling Silverlight Controls.)

4. Looking at our template

If you look at the “Objects and Timeline” panel in Expression Blend, you’ll see very little in the way of structure. The only thing that is provided is a Grid. If you open your “States” window, you’ll find that a button has several different states. This is where we will be spending most of our time.

5. Adding an image to our template

I called this Template an ImageButton, because I am going to create a button that has a consistent image in the background. So, to add an image, just double-click on the Image control in your toolbox.

I recommend stretching it to the size of your template…that way, your button will be proportional when you create other sizes in your application. Also, you need to add an image to your project that you’re going to use. For my example, I’m using a picture of my face. Think of it as a public service for you to hit me in the face over and over.🙂

6. We also need a ContentPresenter

We need to add a ContentPresenter control, but that’s what it is. This will show the “Content” property of our buttons. I’m putting this control on my forehead. Here’s what it looks like:

If you go back to Visual Studio and run this project, you should now have a button on your page that instead looks like your image, but still displays your button Content. Give it a try. Mine says “Click Me!” like the initial XAML I gave you.

7. Playing with States

If you open the States window in Expression Blend 3, you’ll see that we already have a bunch of States defined for a button. We’re going to be modifying some of these, so that our ImageButton actually does something. You may have noticed that while our Content showed up appropriately, the button didn’t have that “click” feel we’re used to. This will fix that. Here’s a shot of the States window:

What we are going to do is add some behaviors to the “Pressed” state so that the button reacts when it’s clicked.

8. Adding the Pressed state

In the States window, click on the “Pressed” item. You’ll notice that immediately the design pane is outlined in red. This is because we’ve started recording our actions. The changes you make at this point will be the changes that happen when you “Press” the button. In my example, I am going to have my Image control change its opacity. At this point, we’re just creating a simple animation. Look back to Day #11 in this series to see more about animation. I am going to create an animation that changes the opacity from 100% to 33% in just 0.1 seconds. Here’s what the entire contents of my App.xaml file looks like now:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="SilverlightTemplating.App"
mc:Ignorable="d"
>
<
Application.Resources>

<
ControlTemplate x:Key="ImageButton" TargetType="Button">
<
Grid Height="200" Width="200">
<
VisualStateManager.VisualStateGroups>
<
VisualStateGroup x:Name="CommonStates">
<
VisualState x:Name="Pressed">
<
Storyboard>
<
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<
EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
<
EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.33"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
</
VisualState>
</
VisualStateGroup>
</
VisualStateManager.VisualStateGroups>
<
Image x:Name="image" Source="jeffblankenburgheadshot.jpg" Width="200" Height="200"/>
<
ContentPresenter Height="24" VerticalAlignment="Top" Margin="53,60,53,0"/>
</
Grid>
</
ControlTemplate>
</
Application.Resources>
</
Application>

You may notice, when you run this project, that when you click on the button, it goes transparent, but when you release the mouse, it doesn’t “un-transparentize.” We need to tell it to do that as well. This is a completely open slate we’re working with, and nothing is assumed in a template. This is a plus, however, because although we are working in blank template, if we like 90% of a certain control, we can use that as our starting point. Let’s add the “MouseOver” state now.

9. Adding the “MouseOver” state

Since we want the button to return to its original state when we lift the mouse button, we also create a MouseOver state. This is the state that the control will revert to when we lift the mouse button. I actually copied and pasted the XAML from my Pressed state into this new MouseOver state, and changed the opacity values. Here’s my new XAML:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
x:Class="SilverlightTemplating.App"
>
<
Application.Resources>

<
ControlTemplate x:Key="ImageButton" TargetType="Button">
<
Grid Height="200" Width="200">
<
VisualStateManager.CustomVisualStateManager>
<
ic:ExtendedVisualStateManager/>
</
VisualStateManager.CustomVisualStateManager>
<
VisualStateManager.VisualStateGroups>
<
VisualStateGroup x:Name="CommonStates">
<
VisualState x:Name="MouseOver">
<
Storyboard>
<
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<
EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
</
VisualState>
<
VisualState x:Name="Pressed">
<
Storyboard>
<
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
<
EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.33"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
</
VisualState>
</
VisualStateGroup>
</
VisualStateManager.VisualStateGroups>
<
Image x:Name="image" Source="jeffblankenburgheadshot.jpg" Width="200" Height="200"/>
<
ContentPresenter Height="24" VerticalAlignment="Top" Margin="53,60,53,0"/>
</
Grid>
</
ControlTemplate>
</
Application.Resources>
</
Application>

Summary

In short, this is a robust piece of functionality in Silverlight. Not only can we completely overhaul any of the controls, but we can also use them as templates for future, robust controls that we may imagine tomorrow. If there’s ANYTHING you don’t like about the default controls in Silverlight, you have the power to change them. That is incredibly liberating. Here’s what my new pseudo-button looks like:

Also, if you would like to download the source code for this Silverlight templating example, you can get it here.

Day #26: Silverlight Data Grid

Welcome to day #26 in my series called the 31 Days of Silverlight. Today, we’re going to be talking about how to use a DataGrid control in your Silverlight applications. Let’s get right to it.

1. Add a new reference to your Silverlight project

In order to use the Silverlight DataGrid control, we first need to add a new reference to our project. Go find System.Windows.Controls.Data, and add a reference to that assembly to your project. Make sure you’re only including this reference if you’re using a DataGrid. It’s sizeable, and you really don’t want the burden of this DLL unless you fully intend to use it.

2. Add the XML namespace to your XAML.

In order to use that reference, we need to add the XML namespace for that assembly to our page. This will allow us to add an actual DataGrid to our page. Here’s what my entire XAML page looks like before we begin:

<UserControl x:Class="SilverlightDataGrid.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<
Canvas x:Name="LayoutRoot">

</
Canvas>
</
UserControl>

3. Add a DataGrid to your page

Adding the DataGrid is the simple part of this tutorial. We’ll spend most of our time today manipulating its data. To add the grid to the page, however, use this little snippet of code inside your <Canvas>:

<data:DataGrid x:Name="citygrid" AutoGenerateColumns="True" />

Now, before you get too excited and start running your project, remember that we haven’t added any data to this DataGrid yet. We should probably do that first.

4. Adding data to the DataGrid from code

In order to have some data with some substance, I have set up a new Class file that defines a simple “Location.” Here’s how it looks:

public class Location
{
//Fields
private string _city;
private int _population;
private double _latitude;
private double _longitude;
private double _elevation;

//Properties
public string City
{
get { return _city; }
set { _city = value; }
}

public int Population
{
get { return _population; }
set { _population = value; }
}

public double Latitude
{
get { return _latitude; }
set { _latitude = value; }
}

public double Longitude
{
get { return _longitude; }
set { _longitude = value; }
}

public double Elevation
{
get { return _elevation; }
set { _elevation = value; }
}
}

Next, I’m going to create a List of Locations, and use that as my data source. For my locations, I have the population, elevation, and latitude/longitude or the 16 largest cities in the United States (according to the 2000 US Census). Here’s my data:

List<Location> locations = new List<Location>{
new Location {City="New York, NY", Population=8363710, Latitude=40.714269, Longitude=-74.005973, Elevation=10},
new Location {City="Los Angeles, CA", Population=3833995, Latitude=34.052234, Longitude=-118.243685, Elevation=71},
new Location {City="Chicago, IL", Population=2853114, Latitude=41.850033, Longitude=-87.650052, Elevation=182},
new Location {City="Houston, TX", Population=2242193, Latitude=29.763284, Longitude=-95.363271, Elevation=13},
new Location {City="Phoenix, AZ", Population=1567924, Latitude=33.448377, Longitude=-112.074037, Elevation=340},
new Location {City="Philadelphia, PA", Population=1540351, Latitude=39.952335, Longitude=-75.163789, Elevation=12},
new Location {City="San Antonio, TX", Population=1351305, Latitude=29.424122, Longitude=-98.493628, Elevation=198},
new Location {City="Dallas, TX", Population=1279910, Latitude=32.783330, Longitude=-96.800000, Elevation=131},
new Location {City="San Diego, CA", Population=1279329, Latitude=32.715329, Longitude=-117.157255, Elevation=22},
new Location {City="San Jose, CA", Population=948279, Latitude=37.339386, Longitude=-121.894955, Elevation=26},
new Location {City="Detroit, MI", Population=912062, Latitude=42.331427, Longitude=-83.045754, Elevation=183},
new Location {City="San Francisco, CA", Population=808976, Latitude=37.774929, Longitude=-122.419415, Elevation=16},
new Location {City="Jacksonville, FL", Population=807815, Latitude=30.332184, Longitude=-81.655651, Elevation=5},
new Location {City="Indianapolis, IN", Population=798382, Latitude=39.768377, Longitude=-86.158042, Elevation=218},
new Location {City="Austin, TX", Population=757688, Latitude=30.267153, Longitude=-97.743061, Elevation=149},
new Location {City="Columbus, OH", Population=754885, Latitude=39.961176, Longitude=-82.998794, Elevation=275}
};

Once we have some data to work with, we just need to set the ItemsSource property of our grid. I named my grid “citygrid,” so our statement will look like this:

citygrid.ItemsSource = locations;

If you run your project at this point, you should have a working grid, with all of the data populating it.

5. Using “some” of the data

Much like we did in Day #15, we’re going to use LINQ to select the cities we want. In this case, perhaps we only want cities with more than 1,000,000 citizens. Here’s how you can modify your ItemsSource statement to select just a portion of the entire set of data:

            citygrid.ItemsSource = from location in locations
where location.Population > 1000000
orderby location.City, location.Population
select location;

6. Checking out the built in functionality

If you haven’t run your project yet, give it a try. You should see a grid on your page, with either some or all of your data, depending on what you did in step #5. The grid has some built in functionality that makes our lives a little easier, and required ZERO code.

The first thing is that the columns auto-sort. So you can click on a column heading, and it will sort your cities, populations, etc. from top to bottom, and reversed.

Second, the cells are editable. So you can change the values we have stored in code without writing anything additionally. If you want to turn that off, just add the IsReadOnly=”True” property to your grid.

Third, the grid also has native validation. So, for example, if you try to edit one of the longitude cells, and enter a string for a value, it will prompt you that the value you entered is invalid. A nice bit of functionality for free, huh? Here’s my working Silverlight DataGrid in action (see it in a new window):

You can download my Silverlight DataGrid application here.

7. Summary

In short, it’s incredibly refreshing to have a DataGrid control for Silverlight. There’s thousands of business applications for this, and if you dig a little deeper, you’ll discover just how customizable this control really is. It doesn’t have to look like a grid at all, to be honest. Dan Wahlin has a good tutorial on customizing a grid to look like a timesheet application.