#969 – Items Property of ListBox Contains List of Items

Deriving from an ItemsControl, a ListBox provides access to the list of items contained in the ListBox by way of its Items property.  The Items property is an ItemCollection, which supports the IList and ICollection interfaces, as well as providing properties for grouping, sorting and filtering items in the collection.

Entries in the ItemCollection can be any .NET object (derives from System.Object).  You’ll most often use data binding to bind the ItemsSource property of the ListBox to some sort of collection that supports IEnumerable.  (E.g. an ObservableCollection).  Doing this will set Items to refer to the bound collection.

You can access individual items from code using an indexer on the Items property.

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Note: Our ListBox contains instances of Actor objects
    Actor actor = lbMyListBox.Items[0] as Actor;
    if (actor != null)
        MessageBox.Show(string.Format("First Actor in list is: {0}", actor.FullName));
}

969-001

#968 – ListBox Data Binding Basics, part V

If you want to display the items in a ListBox using something more than a simple string, you can set the ItemTemplate of the ListBox and define the exact layout of each item.  When you set the ItemTemplate, you don’t set the DisplayMemberPath property.  DisplayMemberPath simple defines the template for each item in the ListBox to be a TextBlock that displays a single string.

In the example below, we set an item template for a ListBox that binds to a collection of Actor objects so that we display an image and some information about the actor, for each item.

        <ListBox Margin="15" Width="250" Height="250"
                 ItemsSource="{Binding ActorList}"
                 SelectedItem="{Binding SelectedActor}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Image}" Height="80"/>
                        <StackPanel Margin="5">
                            <TextBlock Text="{Binding FullName}" FontSize="12" FontWeight="Bold"/>
                            <TextBlock Text="{Binding Dates}"/>
                            <TextBlock Text="{Binding KnownFor}" Margin="0,5,0,0" FontStyle="Italic"/>
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

968-001

#967 – ListBox Data Binding Basics, part IV

In the earlier posts, we created an Actor class and then populated a list with a series of Actor objects.  We can now bind a ListBox to this list using its ItemsSource property.

    <StackPanel>
        <ListBox Margin="15" Width="250" Height="250"
                 ItemsSource="{Binding ActorList}"
                 DisplayMemberPath="NameAndDates"
                 SelectedItem="{Binding SelectedActor}"/>
        <Label Margin="10,0" Content="{Binding SelectedActor.KnownFor}"/>
    </StackPanel>

The ItemsSource property indicates that we want the ListBox filled with elements from our ActorList property, which is a collection of Actor objects.

The DisplayMemberPath property indicates that each entry in the list should be rendered as a string using the Actor.NameAndDates property.

The SelectedItem property indicates that when a user selects an item, our SelectedActor property should be set to refer to the selected Actor instance.  We demonstrate that by binding a Label element to a property of that selected actor.

967-001

#966 – ListBox Data Binding Basics, part III

Assume that we want to use a ListBox to display a list of actors and that we have an Actor type to store all the information for a single actor.

Our next step is to create a collection of Actor instances that we’ll then be able to bind to.  Below is the code-behind for a simple WPF application that creates a collection of Actor instances and stores them in an ActorList property.  We also set up a SelectedActor property that will use data binding to reflect the actor that the user has currently selected.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            this.InitializeComponent();
            this.DataContext = this;

            ActorList = new ObservableCollection<Actor>
            {
                new Actor("Ginger Rogers", 1911, 1995, "Kitty Foyle",
                    new Uri("ActressImages/GingerRogers.jpg", UriKind.Relative)),
                new Actor("Joan Fontaine", 1917, null, "Suspicion",
                    new Uri("ActressImages/JoanFontaine.jpg", UriKind.Relative)),
                new Actor("Greer Garson", 1904, 1996, "Mrs. Miniver",
                    new Uri("ActressImages/GreerGarson.jpg", UriKind.Relative)),
                new Actor("Jennifer Jones", 1919, 2009, "The Song of Bernadette",
                    new Uri("ActressImages/JenniferJones.jpg", UriKind.Relative)),
                new Actor("Ingrid Bergman", 1915, 1982, "Gaslight",
                    new Uri("ActressImages/IngridBergman.jpg", UriKind.Relative)),
                new Actor("Joan Crawford", 1904, 1977, "Mildred Pierce",
                    new Uri("ActressImages/JoanCrawford.jpg", UriKind.Relative)),
                new Actor("Olivia de Havilland", 1916, null, "To Each His Own",
                    new Uri("ActressImages/OliviaDeHavilland.jpg", UriKind.Relative)),
                new Actor("Loretta Young", 1913, 2000, "The Farmer's Daughter",
                    new Uri("ActressImages/LorettaYoung.jpg", UriKind.Relative)),
                new Actor("Jane Wyman", 1917, 2007, "Johnny Belinda",
                    new Uri("ActressImages/JaneWyman.jpg", UriKind.Relative)),
                new Actor("Judy Holliday", 1921, 1965, "Born Yesterday",
                    new Uri("ActressImages/JudyHolliday.jpg", UriKind.Relative))
            };
        }

        private ObservableCollection<Actor> actorList;
        public ObservableCollection<Actor> ActorList
        {
            get { return actorList; }
            set
            {
                if (value != actorList)
                {
                    actorList = value;
                    RaisePropertyChanged("ActorList");
                }
            }
        }

        private Actor selectedActor;
        public Actor SelectedActor
        {
            get { return selectedActor; }
            set
            {
                if (value != selectedActor)
                {
                    selectedActor = value;
                    RaisePropertyChanged("SelectedActor");
                }
            }
        }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

#965 – ListBox Data Binding Basics, part II

Assume that we want to use a ListBox to display a list of actors.  We can start by creating an Actor type that stores various information about an actor.  We want the class to implement the INotifyPropertyChanged interface, so that data binding client are notified of changes to any properties.

Full code for Actor.cs is shown below.  Note that when we change FullName, BirthYear or DeathYear properties, we also flag the derived NameAndDates property as potentially changed.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace WpfApplication2
{
    public class Actor : INotifyPropertyChanged
    {
        public Actor()
        {
        }

        public Actor(string fullName, int? birthYear, int? deathYear, string knownFor, Uri image)
        {
            FullName = fullName;
            BirthYear = birthYear;
            DeathYear = deathYear;
            KnownFor = knownFor;
            Image = image;
        }

        private string fullName;
        public string FullName
        {
            get { return fullName; }
            set
            {
                fullName = value;
                RaisePropertyChanged("FullName");
                RaisePropertyChanged("NameAndDates");
            }
        }

        private int? birthYear;
        public int? BirthYear
        {
            get { return birthYear; }
            set
            {
                birthYear = value;
                RaisePropertyChanged("BirthYear");
                RaisePropertyChanged("NameAndDates");
            }
        }

        private int? deathYear;
        public int? DeathYear
        {
            get { return deathYear; }
            set
            {
                deathYear = value;
                RaisePropertyChanged("DeathYear");
                RaisePropertyChanged("NameAndDates");
            }
        }

        private string knownFor;
        public string KnownFor
        {
            get { return knownFor; }
            set
            {
                knownFor = value;
                RaisePropertyChanged("KnownFor");
            }
        }

        private Uri image;
        public Uri Image
        {
            get { return image; }
            set
            {
                image = value;
                RaisePropertyChanged("Image");
            }
        }

        public string NameAndDates
        {
            get
            {
                string result = FullName;

                if (BirthYear.HasValue)
                {
                    if (DeathYear.HasValue)
                        result = result + string.Format(" ({0}-{1})", BirthYear.Value, DeathYear.Value);
                    else
                        result = result + string.Format(" ({0}- )", BirthYear.Value);
                }

                return result;
            }
        }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

#964 – ListBox Data Binding Basics, Part I

You can use data binding to load and manage the items displayed in a ListBox control.

You bind the ItemsSource property of the ListBox to a collection that implements the IEnumerable interface.  The collection bound to can contain any type of object.

If the ListBox is displaying simple strings, you can set the DisplayMemberPath property to the string-typed property of a bound object  that should be used to get the display string for each item.

You can also use binding to bind the SelectedItem property of the ListBox to a property whose type matches the types in the collection that ItemsSource binds to.  When the user selects an item in the ListBox, the corresponding property is updated to refer to the correct item.  And if the property bound to is changed from code-behind, the selected item in the ListBox changes.

Next time: Code sample for all of this.

#963 – A Color Selection Box Organized by Hue, part II

In part I, I included source code for generating a list of sample colors.

To bind to a generated color list, we can call ColorUtil.GenerateColorList and make the result available via a property.  In the code sample below, we generate a list of 4,096 colors (16 values for red, green and blue).

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        this.InitializeComponent();
        this.DataContext = this;

         ColorList = ColorUtil.GenerateColorList(16);
    }

    private List<ColorWithInfo> colorList;
    public List<ColorWithInfo> ColorList
    {
        get { return colorList; }
        protected set
        {
            colorList = value;
            RaisePropertyChanged("ColorList");
        }
    }

    // INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private void RaisePropertyChanged(string propName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

In our XAML, we can create a simple ComboBox that binds to this list.  We set the ItemTemplate to display each color as a simple rectangle and the ItemsPanel as a WrapPanel with a fixed width.

    <ComboBox Margin="15" Height="30" Width="200"
                ItemsSource="{Binding ColorList}"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Rectangle Height="55" Width="55">
                        <Rectangle.Fill>
                            <SolidColorBrush Color="{Binding Color}"/>
                        </Rectangle.Fill>
                    </Rectangle>
                </Grid>
            </DataTemplate>
        </ComboBox.ItemTemplate>
        <ComboBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="1000"/>
            </ItemsPanelTemplate>
        </ComboBox.ItemsPanel>
    </ComboBox>

Below is an image showing the ComboBox in action. Note that the colors are sorted by hue, given that the GenerateColorList method sorted the color values this way.

963-001