#828 – ListView and GridView Data Binding Example

ListView control contains a collection of items that you can view in different ways.  The current view can be set using the View property, which can be set to an instance of a GridView.  A GridView is an object that can display the data in columns.

Below is a complete example of a bound ListView that uses a GridView to display its items.  The ListView is bound to a collection and the GridView contains GridViewColumn instances, each of which is bound to a property of the items contained in the collection.

    <StackPanel>
        <Label Content="Ad eundum quo nemo ante iit"
               Margin="5"
               Background="LightCoral"/>
        <ListView ItemsSource="{Binding Guys}">
            <ListView.View>
                <GridView>
                    <GridViewColumn DisplayMemberBinding="{Binding Name}"
                                    Header="Guy"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Age}"
                                    Header="Age"/>
                </GridView>
            </ListView.View>
        </ListView>
    </StackPanel>

The code-behind for the top-level Window sets the data context and creates the collection.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public ObservableCollection<Person> Guys { get; protected set; }

        public MainWindow()
        {
            this.InitializeComponent();
            this.DataContext = this;

            Guys = new ObservableCollection<Person>();
            Guys.Add(new Person("Julius Caesar", 40));
            Guys.Add(new Person("Pompeius Magnus", 46));
            Guys.Add(new Person("Marcus Crassus", 55));
            RaisePropertyChanged("Guys");
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

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

For completeness, here is the implementation of the Person class.

    public class Person :  INotifyPropertyChanged
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

828-001

#825 – Two Way Binding for a CheckBox

You can bind the IsChecked property of a CheckBox to a boolean variable, so that the variable will always reflect the current value of the CheckBox in the user interface.

You can also do two-way binding, where the boolean variable changes when the user toggles the CheckBox, but the CheckBox also toggles when the value of the variable changes.

        <Label Content="Things my dog can do:"/>
        <CheckBox Content="Sit" IsChecked="{Binding CanSit, Mode=TwoWay}"/>
        <CheckBox Content="Stay" IsChecked="{Binding CanStay, Mode=TwoWay}"/>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Content="Report State" Click="btnReportState_Click"
                    Margin="5"/>
            <Button Content="Change State" Click="btnChangeState_Click"
                    Margin="5"/>
        </StackPanel>

Code-behind:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private bool canSit;
        public bool CanSit
        {
            get { return canSit; }
            set
            {
                canSit = value;
                RaisePropertyChanged("CanSit");
            }
        }

        private bool canStay;
        public bool CanStay
        {
            get { return canStay; }
            set
            {
                canStay = value;
                RaisePropertyChanged("CanStay");
            }
        }

        public MainWindow()
        {
            this.InitializeComponent();
            this.DataContext = this;
        }

        private void btnReportState_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("Sit: {0}, Stay: {1}", CanSit, CanStay));
        }

        private void btnChangeState_Click(object sender, RoutedEventArgs e)
        {
            CanSit = CanSit ? false : true;
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

#786 – Using Data Binding to Control a 2D Transform

It’s common to use data binding to control the values used for 2D transformations.  Below is a simple example that binds the AngleCenterX and CenterY properties of a RotateTransform to corresponding properties in a class.

    <StackPanel Margin="20,80">
        <Label Content="Dwight D. Eisenhower (1890-1969)" Background="Plum" Margin="10"
               HorizontalAlignment="Center" Padding="20,10" FontSize="16">
            <Label.LayoutTransform>
                <RotateTransform  Angle="{Binding IkeAngle}"
                                  CenterX="{Binding IkeCenterX}"
                                  CenterY="{Binding IkeCenterY}" />
            </Label.LayoutTransform>
        </Label>
    </StackPanel>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            // Test data
            IkeAngle = 25;
            IkeCenterX = 0.0;
            IkeCenterY = 0.0;
        }

        public double IkeAngle { get; set; }
        public double IkeCenterX { get; set; }
        public double IkeCenterY { get; set; }

        //-- INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

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

786-001

#766 – WPF Data Binding Ignores Custom Formatting

You can get the WPF data binding engine to respect the current locale, as specified in the Region applet, by setting the Language property of a FrameworkElement to the language tag that is part of the CurrentCulture property.

Doing this allows you to set up the proper locale in a single line of code and data binding will use the correct rules for formatting dates and floating point numbers for the appropriate language.

This mechanism won’t, however, honor any custom formatting that you specify in the Region applet.  A custom format is anything that you change from the defaults in the Region applet.

766-001

If you want the data binding engine to completely respect all of the settings chosen in the Region applet, you can set the ConverterCulture property to the CurrentCulture, for each binding.

        <TextBlock Text="{Binding BoundDate, ConverterCulture={x:Static glob:CultureInfo.CurrentCulture}}"/>

The glob namespace points to System.Globalization.

766-002

#765 – WPF Data Binding Ignores CurrentCulture

Using the ToString method on numeric and date-based types from within your code, the output will reflect the current culture, as stored in the CurrentCulture property of the current thread.

When the data binding mechanism in WPF converts numeric or date-based data to a string, however, it doesn’t automatically use the current culture.  Instead, you must set the Language property of the FrameworkElement where the binding is occurring.  You typically set this property based on the CurrentCulture property of the current thread.  You can set the Language property of the topmost user-interface element, so that all of the other elements inherit the value.

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
        }

We can now do binding as follows (where BoundDate property is of type DateTime):

        <TextBlock Text="{Binding BoundDate, StringFormat=d}"/>

Now if we run with the region set to French (France), we see:
765-001

#495 – Binding to a Visibility Property Without Using a Value Converter

You can use a value converter to convert a string value to a value of type System.Windows.Visibility, allowing data binding to a Visibility property.  You can also just set a Visibility property directly to a string, avoiding the need for a value converter.

In the example below, we bind to the Content property of a ComboBoxItem, which we access through the SelectedValue property of a ComboBox.  Since Content contains a string, the binding to Visibility works without a conversion.

    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="Snoopy" Margin="3" Background="BurlyWood"/>
            <Label Content="Waldo" Margin="3" Background="Thistle"
                   Visibility="{Binding ElementName=cboVisibility, Path=SelectedValue.Content}"/>
            <Label Content="Dagwood" Margin="3" Background="LightGreen"/>
        </StackPanel>
        <ComboBox Name="cboVisibility" HorizontalAlignment="Center" SelectedIndex="0">
            <ComboBox.Items>
                <ComboBoxItem Content="Visible"/>
                <ComboBoxItem Content="Collapsed"/>
                <ComboBoxItem Content="Hidden"/>
            </ComboBox.Items>
        </ComboBox>
        <Label Content="Select visibility of middle Label" HorizontalAlignment="Center"/>
    </StackPanel>

#485 – Binding a ComboBox to an Enumerated Type’s List of Values

You can easily bind a ComboBox (or ListBox) to an enumerated type’s values using an ObjectDataProvider.  The ObjectDataProvider allows calling a method and then using the result of the method call as a binding source.

To get the list of values for an enumerated type, we call the Enum.GetValues method, passing in the specific enumerated type.  In the example below, we do this in an ObjectDataProvider.

    <Window.Resources>
        <ObjectDataProvider x:Key="dateTimeKindValues" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="sys:DateTimeKind"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

(This assumes that we’ve defined the sys namespace):

    xmlns:sys="clr-namespace:System;assembly=mscorlib"

The ObjectDataProvider is equivalent to the following code:

            DateTimeKind[] values = (DateTimeKind[])Enum.GetValues(typeof(DateTimeKind));

We can now use the output of the ObjectDataProvider as a data source for our binding.

    <ComboBox Height="25" Width="150"
              ItemsSource="{Binding Source={StaticResource dateTimeKindValues}}"/>

This gives us a ComboBox that lists the possible values of the DateTimeKind enumerated type.

#452 – Data Binding Elements in a Collection to a Grid, Part V

We saw earlier that to bind Grid.Row and Grid.Column properties on child items in an ItemsControl,  we need to set up the binding on the ContentPresenter elements that contain the individual items.

We can do this by overriding the ItemsControl.PrepareContainerForItemOverride method.  This method is called for each item in the collection and receives both the containing element (a ContentPresenter in our case) and the contained item (a ChessPiece object in our example).  We bind the Grid.Row and Grid.Column properties on the ContentPresenter to the same properties on the contained item.

public class GridBasedItemsControl : ItemsControl
    {
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            ContentPresenter cp = element as ContentPresenter;

            if ((cp != null) && (item != null))
            {
                BindingOperations.SetBinding(cp, Grid.RowProperty, new Binding { Source = item, Path = new PropertyPath("Row")});
                BindingOperations.SetBinding(cp, Grid.ColumnProperty, new Binding { Source = item, Path = new PropertyPath("Column") });
            }

        }
    }

Everything now works as expected.

NOTE: Reader Bruno pointed out a much better way to do this, using the ItemContainerStyle property.

#451 – Data Binding Elements in a Collection to a Grid, Part IV

Our first attempt to bind elements in a collection to a Grid didn’t work as expected.  We set the Grid.Row and Grid.Column properties in the data template, but the items did not show up in the correct row and column.

The problem is that when we’re setting the Grid.Row and Grid.Column properties on the Label control in the data template, these Label controls are not direct children of the Grid, so the properties have no effect.

We can look at the visual tree of the ItemsControl to see this.  The direct child elements of the Grid are actually instances of a ContentPresenter.

What we really want to do is set the value of the Grid.Row and Grid.Column properties on the ContentPresenter, rather than the Label.  I’ll show how to do that in the next post.

 

#450 – Data Binding Elements in a Collection to a Grid, part III

We’ve done part of the work required in order to bind a collection of elements to a Grid, with each object’s Row and Column properties indicating where in the Grid it should be located.

  • Set the ItemsPanel property of an ItemsControl to represent a Grid
  • Set the ItemTemplate property to represent the item, setting the Grid.Row and Grid.Column property for each

At this point, the binding doesn’t work as expected.  Each element does get inserted into the grid, but every element is added to Row 0 and Column 0.  The Grid.Row and Grid.Column attached properties are being ignored.