Developer

Windows Phone 7: binding custom ListBox data

To make useful Windows Phone apps, one must master how to add data into ListBoxes.

The final look we are aiming for.

In a previous Windows Phone article, we looked at how to customise a ListBox Style element to include interface widgets. This time, we'll take a step back and begin to customise the data that is shown.

To begin, create a default Panorama application, or you can either use or recycle the code from the previous article — I'll be re-using the same codebase as the last article.

The ListBox I'll be customising is the one under the "second item" heading that holds the "runtime one" to "runtime sixteen" items, with red squares beside them. The end result will be a list of URLs that launch an Internet Explorer session when an item is selected.

So that we can operate on the ListBox in the code-behind file that contains the C# code, MainPage.xaml.cs for MainPage.xaml, we must give the ListBox a Name property in its XAML declaration. I chose the unimaginative name "SecondLB".

It should come as no surprise to learn that ListBoxes handle lists. Therefore, to add any data we want into a ListBox, we only need to create the list we want and set it as the ListBox's ItemsSource. Since we are going to show a list of websites, a quick list of custom objects that stores website details will do nicely.

The custom object is called Website, and looks like this:
public class Website

{

public String Name { get; set; }

public Uri Url { get; set; }

public String Description { get; set; }

public Uri ImageUrl { get; set; }

public Website(String name, Uri url , String description, Uri imageurl)

{

this.Name = name;

this.Url = url;

this.Description = description;

this.ImageUrl = imageurl;

}

}
Now let's populate the ListBox when the MainPage has loaded, we do this by adding to the MainPage_Loaded method:
List<Website> sites = new List<Website>();

sites.Add(

new Website(

"Google",

new Uri("http://www.google.com/"),

"Search and Ad engine",

new Uri("https://www.google.com.au/logos/classicplus.png")

)

);

sites.Add(

new Website(

"Microsoft",

new Uri("http://www.microsoft.com"),

"They made this",

new Uri("http://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Microsoft.svg/220px-Microsoft.svg.png")

)

);

sites.Add(

new Website(

"Girt By Code",

new Uri("http://www.techrepublic.com/blog/australia"),

"The blog",

new Uri("http://i3.trstatic.com/images/201101/bkg-logo-header-227x43.png")

)

);

sites.Add(

new Website(

"Apple",

new Uri("http://www.apple.com"),

"A company of some note",

new Uri("http://upload.wikimedia.org/wikipedia/en/thumb/9/9f/Apple-logo.svg/200px-Apple-logo.svg.png")

)

);

SecondLB.ItemsSource = sites;

If we run the app in the emulator, you'll see that the ListBox is empty, except for the red squares. That's a result of setting the ItemsSource, but not declaring which XML properties of each Website object the ListBox should show.

Fixing the TextBlock items is easy. Change the Bindings from LineOne and LineTwo to Name and Description, respectively.
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>

Replacing the red square with an image for each website is a little tricker. To load an image from a URL, we need to create a BitmapImage and load the URL into its UriSource property. That BitmapImage is wrapped inside of the regular Image's Source property. As the Website object stores the URL of the image we wish to use in its ImageUrl property, we will just need to create the correct binding:

<Image Height="100" Width="100" Margin="12,0,9,0">

<Image.Source>

<BitmapImage UriSource="{Binding ImageUrl}" />

</Image.Source>

</Image>

When we run the app now, the sites are shown correctly.

Feel free, at any point, to change the "second item" header on the second PanoramaItem to "sites" or another name you deem appropriate.

It's time to open Internet Explorer when a site is selected. Add to the ListBox XAML declaration:

SelectionChanged="SecondLB_SelectionChanged"

That will call the SecondLB_SelectionChanged method whenever a user selects an item in the ListBox.

In the C#, we will take the selected item, listBox.SelectedItem, cast it to our Website object and pass it to Internet Explorer via the WebBrowserTask class in the Microsoft.Phone.Tasks namespace.

private void SecondLB_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

ListBox listBox = sender as ListBox;

if (listBox != null && listBox.SelectedItem != null)

{

Website sItem = (Website)listBox.SelectedItem;

WebBrowserTask webBrowserTask = new WebBrowserTask();

webBrowserTask.Uri = sItem.Url;

webBrowserTask.Show();

}

}

The empty white space below the content shouldn't be there.

Now when we run the application, the various sites load as expected in a full Internet Explorer instance.

On occasion, a bug appears that will paint Internet Explorer's menu and address bar as a white block. The current wisdom is that this is due to having the debugger attached, and when the debugger is not used, it will paint correctly.

When loading a site in IE and hitting the back button to return to our program, you will notice that if you select the same site again, it does not load. This is due to ListBox remembering the selection you made when it launched the WebBrowserTask. As our event listener is tied to the SelectionChanged event and the selection is not changing, the event fails to fire.

A simple fix is all that is needed. When the user presses the back button from IE and returns of our app, it calls the Page.OnNavigatedTo method. If we set the ListBoxes selected index to minus one, we can then reselect the same element when returning to the page.

protected override void OnNavigatedTo(NavigationEventArgs e)

{

base.OnNavigatedTo(e);

SecondLB.SelectedIndex = -1;

}

And, there we go; enjoy binding ListBoxes to your heart's content.

About

Some would say that it is a long way from software engineering to journalism, others would correctly argue that it is a mere 10 metres according to the floor plan.During his first five years with CBS Interactive, Chris started his journalistic advent...

Editor's Picks