#1,140 – Using a Value Converter in a Template

You can use a value converter anywhere in XAML where you are using data binding.

Below is an example of using a value converter within a data template.  The Visibility property is bound to the underlying Actor object that is the data context for the item template.  The value converter then derives a value for Visibility from several properties within the Actor object.  (Assume that we have an ActorList property that is a collection of Actor instances).

The XAML includes:

    <Window.Resources>
        <loc:DeadFredConverter x:Key="deadFredConverter"/>
    </Window.Resources>

    <StackPanel>
        <ListBox Margin="15" Width="270" Height="320"
             ItemsSource="{Binding ActorList}">
            <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>
                        <Label Content="Dead Fred !" Foreground="Red"
                               FontWeight="Bold"
                               Visibility="{Binding Converter={StaticResource deadFredConverter}}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>

The body of the value converter is:

    class DeadFredConverter : IValueConverter
    {
        // Convert to Visibility, deriving from properties on Actor object
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Actor a = (Actor)value;

            Visibility vis = Visibility.Hidden;

            if ((a.FirstName == "Fred") &&
                a.DeathYear.HasValue &&
                (a.DeathYear <= DateTime.Today.Year))
                vis = Visibility.Visible;

            return vis;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

1140-001

#1,139 – Value Converter Example for Input

The ConvertBack method of a value converter is used to convert from a binding target (e.g. the attribute of a control) to a binding source (e.g. a property).  Below is a simple example, showing how we can convert from a Slider value to the square root of the selected value.

In XAML, we have a Slider that ranges from 1-100 and binds to a property that is meant to store the square root of the selected value.  We specify a binding Mode to indicate that binding should only map from the target (the Value property) to the source (the SqRootValue property) and not in the other direction.  We then include labels that bind to the Slider’s Value property as well as the SqRootValue property.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToRootConverter x:Key="intToRootConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider x:Name="slider" Minimum="1" Maximum="100"
                IsSnapToTickEnabled="True"
                Value="{Binding Path=SqRootValue,
                                Converter={StaticResource intToRootConverter},
                                Mode=OneWayToSource}"/>
        <Label Content="{Binding ElementName=slider, Path=Value}"/>
        <Label Content="{Binding SqRootValue}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

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

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private double sqRootValue;
        public double SqRootValue
        {
            get { return sqRootValue; }
            set
            {
                if (sqRootValue != value)
                {
                    sqRootValue = value;
                    OnPropertyChanged("SqRootValue");
                }
            }
        }
    }

In the value converter, we just take the square root of the current value, calculating the result in ConvertBack.

    public class IntToRootConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        // Convert from int (target of binding) to double representing square root (source of binding)
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double root = Math.Sqrt((double)value);
            return root;
        }
    }

At run-time:

1139-001

#1,138 – Value Converter Example for Output

The Convert method of a value converter is used to convert from a binding source (e.g. a property) to a binding target (e.g. the attribute of a control).  Below is a simple example, showing how we can convert from an integer to a brush of a particular color.

In XAML, we have a Slider that ranges from 0-255 and binds to a property, storing the integer value that the user selects.  We then bind to the same property for a rectangle shape’s Fill property.  The Fill property wants a Brush object, so we use a value converter to convert from the integer to a brush.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToBlueBrushValueConverter x:Key="intToBlueBrushValueConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider Minimum="0" Maximum="255"
                Value="{Binding BlueValue}"/>
        <Label Content="{Binding BlueValue}"/>
        <Rectangle Height="80" Width="80"
            Fill="{Binding Path=BlueValue, Converter={StaticResource intToBlueBrushValueConverter}}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

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

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private int blueValue;
        public int BlueValue
        {
            get { return blueValue; }
            set
            {
                if (blueValue != value)
                {
                    blueValue = value;
                    OnPropertyChanged("BlueValue");
                }
            }
        }
    }

In the value converter, we create a solid color brush whose R and G values are 0 and whose B value is derived from the integer (slider position).

    public class IntToBlueBrushValueConverter : IValueConverter
    {
        // Convert from int to System.Windows.Media.Brush
        // where brush is color with R=0, G=0 and B=int
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int blueVal = (int)value;
            blueVal = Math.Min(Math.Max(0, blueVal), 255);

            return new SolidColorBrush(Color.FromRgb(0, 0, (byte)blueVal));
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }

At run-time, the color of the rectangle changes as we move the slider.

1138-001

1138-002

1138-003

#1,137 – Value Converter Basics

A value converter is a class that knows how to convert data as part of the data binding process.  It can convert data in two directions:

  • From the data type of a bound property to the data type required by the attribute that is binding to the property (output)
  • From the data type of an attribute to the data type of a bound property (input)

A value converter implements the IValueConverter interface.  IValueConverter.Convert is used for output, converting from a bound property value to the type required by an attribute.  For example, you might have a integer property that you bind to and use in setting the background color of a particular control.

IValueConverter.ConvertBack is used for input, converting from an attribute’s value to a bound property.

#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));
        }
    }
Follow

Get every new post delivered to your Inbox.

Join 378 other followers