Using ListBox

We have shipped source codes for some sample controls as part of the SDK. There was a question on forum about how to use the listbox in the samples so here is the sample.

Few steps to get started...

  1. Create new project of type "Silverlight Project"
  2. Add a reference to Silverlight.Samples.Controls.dll from the SDK which has ListBox control
  3. ListBox.Items property is a collection that takes the item of type FrameworkElement. To make things easy, I just added a new item to my project of type "User Control" and I named it ImageListItem.xaml.
  4. I also created an XML file that contains the data. This XML data can come from anywhere but for making things easy. I added xml file called "ImageListData.xml" to my project and marked it as "Embedded Resources". This way xml file gets embedded in the assembly itself and I read it using resources APIs in Silverlight.
  5. This xml file contains the data about the title of the image and source (uri) to the image file. Image files are also located under project to make things easy but they can reside anywhere as well.

So my ImageListData.xml looks like this...

 <?xml version="1.0" encoding="utf-8" ?>
<ImageList>
  <Image Title="RSS" Source="Images/image1.jpg"/>
  <Image Title="Internet Explorer" Source="Images/image2.jpg"/>
  <Image Title="Outlook" Source="Images/image3.jpg"/>
  <Image Title="Visual Studio" Source="Images/image5.jpg"/>
  <Image Title="Notepad" Source="Images/image6.jpg"/>
  <Image Title="Live Writer" Source="Images/image7.jpg"/>
  <Image Title="Expression Blend" Source="Images/image8.jpg"/>
</ImageList>

ImageListItem is the control that knows how to show an image and a title. Xaml for that looks pretty straight forward too...It has an image element to show the image and TextBlock element to show the title.

 <Canvas xmlns="https://schemas.microsoft.com/client/2007" 
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
        Width="240"
        Height="48"
        Background="#FFB4D8F1"
        >
    <Image x:Name="image"/>
    <TextBlock Canvas.Left="72" Canvas.Top="8" TextWrapping="Wrap" x:Name="title"/>
</Canvas>

ImageListItem.xaml.cs contains the definition for logic which also simple. It only has two properties called Source (of type URI) and Title (of type string).

 using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace ImageListBox
{
    public class ImageListItem : Control
    {
        Canvas _rootCanvas;
        Image _image;
        TextBlock _title;
        public ImageListItem()
        {
            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("ImageListBox.ImageListItem.xaml");
            _rootCanvas = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
            _image = _rootCanvas.FindName("image") as Image;
            _title = _rootCanvas.FindName("title") as TextBlock;
            this.Loaded += new EventHandler(ImageListItem_Loaded);
        }

        void ImageListItem_Loaded(object sender, EventArgs e)
        {
            UpdateLayout();
        }

        public new double Height
        {
            get { return ((FrameworkElement)this).Height; }
            set
            {
                ((FrameworkElement)this).Height = value;
                UpdateLayout();
            }
        }

        public new double Width
        {
            get { return ((FrameworkElement)this).Width; }
            set
            {
                ((FrameworkElement)this).Width = value;
                UpdateLayout();
            }
        }

        private void UpdateLayout()
        {
            _rootCanvas.Height = Height;
            _rootCanvas.Width = Width;
            _image.Height = Height;
            _image.Width = Height;
            _title.Height = Height;
            _title.Width = Width - Height;
        }

        public Uri Source
        {
            get { return _image.Source;}
            set { _image.Source = value; }
        }

        public string Title
        {
            get { return _title.Text; }
            set { _title.Text = value; }
        }
    }
}

Now that I have control that can show an image and a title there are three more things i need to do to make this work...

  1. Use ListBox control
  2. Read XML using XmlReader in System.xml namespace.
  3. Use that data to instantiate ListBoxItems and add them to listbox.

First part is pretty straight forward...

 <Canvas x:Name="parentCanvas"
        xmlns="https://schemas.microsoft.com/client/2007" 
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
        Loaded="Page_Loaded" 
        x:Class="ImageListBox.Page;assembly=ClientBin/ImageListBox.dll"
        xmlns:Controls="clr-namespace:Silverlight.Samples.Controls;assembly=ClientBin/Silverlight.Samples.Controls.dll"
        Width="640"
        Height="480"
        Background="White"
        >
  <Controls:ListBox Height="350" Width="200"  Name="listbox"/>

</Canvas>

in the Page.xml.cs code... 

 using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Silverlight.Samples.Controls;
using System.Xml;
using System.IO;
using System.Windows.Browser.Net;
using System.Net;

namespace ImageListBox
{
    public partial class Page : Canvas
    {
        public void Page_Loaded(object o, EventArgs e)
        {
            // Required to initialize variables
            InitializeComponent();
            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("ImageListBox.ImageListData.xml");
            ListBox listbox = this.FindName("listbox") as ListBox;
            XmlReader xr = XmlReader.Create(s);
            while (xr.Read())
            {

                if (xr.IsStartElement("Image"))
                {
                    string title = xr.GetAttribute("Title");
                    string source = xr.GetAttribute("Source");
                    ImageListItem il = new ImageListItem();
                    Uri u = new Uri(source, UriKind.Relative);
                    il.Source = u;
                    il.Title = title;
                    il.Height = 50;
                    il.Width = 200;
                    listbox.Items.Add(il);
                }
            }
            listbox.UpdateItems();
        }
    }
}

The reason why you need to call  listbox.UpdateItems is because framework still does not have support for ObservableCollections and so ListBox does not get notified when items are added to its "Items" collection. UpdateItems forces it to add all the items in collection to its visual tree.

ImageListBox.zip