#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

#962 – A Color Selection Box Organized by Hue, part I

Here’s some code that generates a spread of different colors.  (We’ll later bind a ComboBox to the generated list of colors).

    public class ColorWithInfo : IComparable
    {
        public Color Color { get; set; }

        public string Info
        {
            get { return string.Format("{0}/{1}/{2}", Color.R, Color.G, Color.B); }
        }

        public string HueSatBright
        {
            get { return string.Format("{0}/{1}/{2}", Hue, Saturation, Brightness); }
        }

        public float Hue
        {
            get { return System.Drawing.Color.FromArgb(Color.R, Color.G, Color.B).GetHue(); }
        }

        public float Saturation
        {
            get { return System.Drawing.Color.FromArgb(Color.R, Color.G, Color.B).GetSaturation(); }
        }

        public float Brightness
        {
            get { return System.Drawing.Color.FromArgb(Color.R, Color.G, Color.B).GetBrightness(); }
        }

        public int CompareTo(object obj)
        {
            ColorWithInfo cwiOther = obj as ColorWithInfo;

            // Sort by Hue, and then Saturation, and then Brightness
            if (this.Hue == cwiOther.Hue)
            {
                if (this.Saturation == cwiOther.Saturation)
                    return this.Brightness.CompareTo(cwiOther.Brightness);
                else
                    return this.Saturation.CompareTo(cwiOther.Saturation);
            }
            else
                return this.Hue.CompareTo(cwiOther.Hue);
        }
    }

    public static class ColorUtil
    {
        public static List<ColorWithInfo> GenerateColorList(int numValsPerColor)
        {
            List<ColorWithInfo> colorList = new List<ColorWithInfo>();

            // Create increment such that we start at 0, end at 255,
            // and have a total of numValsPerColor within that range.
            int delta = Convert.ToInt32(255.0 / ((double)numValsPerColor - 1.0));

            for (int r = 0; r < numValsPerColor; r++)
                for (int g = 0; g < numValsPerColor; g++)
                    for (int b = 0; b < numValsPerColor; b++)
                    {
                        ColorWithInfo cwi = new ColorWithInfo {
                            Color = Color.FromRgb((byte)(r * delta), (byte)(g * delta), (byte)(b * delta))};
                        colorList.Add(cwi);
                    }

            colorList.Sort();

            return colorList;
        }
    }