Friday, February 1, 2019

Binding to SelectedDataItems on XamDataGrid

Unlike the Microsoft data grid the Infragistics XamDataGrid does support binding to the SelectedDataItems property. However, doing so is not as straight forward as you might hope. The SelectedDataItemsScope has a lot of effect and the binding is a bit weird.

Here's a fun project that demostrates how to do the binding and the effect of this property.

Start a new C# project in Visual Studio targeting any framework you want. Call it DataItemsBinding.


We will create a grid containing a combo box that lists the SelectedDataItemsScope enumeration and then a grid that lists all my bicycles. At the bottom is a Text Block that shows how many bicycles are selected.


<Window x:Class="DataItemsBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataItemsBinding"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="Data Items Binding" Height="300" Width="400">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="SelectedDataItemsScope:"/>
        <ComboBox Grid.Row="0" Grid.Column="1"  ItemsSource="{Binding SelectedDataItemScopes}" SelectedItem="{Binding SelectedSelectedDataItemsScope}" IsEditable="False"/>

        <igDP:XamDataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0" DataSource="{Binding Bicycles}" SelectedDataItems="{Binding SelectedBicycles}" SelectedDataItemsScope="{Binding SelectedSelectedDataItemsScope}" GroupByAreaLocation="None">
            <igDP:XamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings SelectionTypeRecord="Extended" AutoGenerateFields="False"/>
            </igDP:XamDataGrid.FieldLayoutSettings>
            <igDP:XamDataGrid.FieldSettings>
                <igDP:FieldSettings AllowSummaries="False" AllowEdit="False" CellClickAction="SelectRecord"/>
            </igDP:XamDataGrid.FieldSettings>
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout>
                    <igDP:TextField Label="Bicycle Type" Name="Type" Width="Auto"/>
                    <igDP:TextField Label="Brand" Name="Brand" Width="Auto"/>
                    <igDP:TextField Label="Year" Name="Year" Width="Auto"/>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>

        <TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding SelectedBicycles.Length, StringFormat='Selected Count = {0}'}"/>
    </Grid>
</Window>

----------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using Infragistics.Windows.DataPresenter;

namespace DataItemsBinding
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {

        public IEnumerable<SelectedDataItemsScope> SelectedDataItemScopes
        {
            get { return Enum.GetValues(typeof(SelectedDataItemsScope)).Cast<SelectedDataItemsScope>(); }
        }

        private SelectedDataItemsScope _SelectedSelectedDataItemsScope;
        public SelectedDataItemsScope SelectedSelectedDataItemsScope
        {
            get { return _SelectedSelectedDataItemsScope; }
            set { SetProperty(ref _SelectedSelectedDataItemsScope, value); }
        }

        public class cBicycle
        {
            public string Type { get; set; }
            public string Brand { get; set; }
            public string Year { get; set; }
        }

        private List<cBicycle> _Bicycles;
        public List<cBicycle> Bicycles
        {
            get { return _Bicycles; }
            set { SetProperty(ref _Bicycles, value); }
        }

        private List<cBicycle> _SelectedBicycles = new List<cBicycle>();
        public object[] SelectedBicycles
        {
            get { return _SelectedBicycles.ToArray(); }
            set { SetProperty(ref _SelectedBicycles, value.ToList().Cast<cBicycle>().ToList()); }
        }

        public MainWindow()
        {
            Bicycles = new List<cBicycle>()
            {
                new cBicycle() {Type="Road", Brand="Serotta", Year="1998"},
                new cBicycle() {Type="Exercise", Brand="Schwinn", Year="2016"},
                new cBicycle() {Type="Mountain", Brand="Fisher", Year="2002"},
                new cBicycle() {Type="Touring", Brand="Trek", Year="2014"},
                new cBicycle() {Type="Classic", Brand="Bianchi", Year="1975"}
            };
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value)) return false;
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        private void OnPropertyChanged(String PropertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
    }
}

Take a look at how we bind a dropdown list to an enumeration. This is a handy trick to know.
public IEnumerable<SelectedDataItemsScope> SelectedDataItemScopes
{
    get { return Enum.GetValues(typeof(SelectedDataItemsScope)).Cast<SelectedDataItemsScope>(); }
}
The rest of the code is quite mundane - properties, classes, more properties, etc.

Now run the application. The default setting for SelectedDataItemsScope is None which means that the SelectedDataItems is never updated, which is an odd design decision. Change SelectedDataItemsScope to RecordsOnly and then select some bicycles - you will see the count of selected bicycles updated below the grid.


Note that selected rows are only indicated by a slight change to the background color and a faint border. By default, the selection indicator to the left only indicates the last selected row.

The SelectedDataItems property is an array of objects. You have to do some work to get them back into a list of bicycles and more work to convert a list of bicycles back to an array.

private List<cBicycle> _SelectedBicycles = new List<cBicycle>();
public object[] SelectedBicycles
{
    get { return _SelectedBicycles.ToArray(); }
    set { SetProperty(ref _SelectedBicycles, value.ToList().Cast<cBicycle>().ToList()); }
}

Try setting CellClickAction="SelectCell" and then selecting "RecordsOrCells" in the drop down list to see how that option works.