How to Bind to the Index of a Collection in WPF

17 באפריל 2010

תגיות: , ,
3 תגובות

Let's say you have a customer class and you are binding a DataGrid to a collection of customers.

You would like the index of the customer in the collection to appear in the first column, like so:

image

One way to do this might be to add an Index property to the Customer class and bind to that.

I prefer not to go that way because it would pollute our business layer with presentation layer constraints.

Solution

Here is a different approach using a MultiBinding.

You can download the source code for this example from here.

This is our DataAccess Layer:

    public class Customer

    {

        public string Name { get; set; }

        public int Age { get; set; }

    }

 

    public class DataAccess

    {

        static private List<Customer> customers;

        static public List<Customer> Customers

        {

            get

            {

                if (customers == null)

                {

                    customers = new List<Customer>

                    {

                        new Customer { Name="Bill", Age=40},

                        new Customer { Name="Alice", Age=35},

                        new Customer { Name="Fred", Age=30},

                        new Customer { Name="Tom", Age=20}

                    };

                }

                return customers;

            }

        }

    }

and here is the XAML:

<DataGrid Name="dg"

         AutoGenerateColumns="False"

         CanUserAddRows="False">

    <DataGrid.Resources>

        <local:IndexConverter x:Key="indexConverter" />

    </DataGrid.Resources>

    <DataGrid.Columns>

        <DataGridTextColumn Header="Index">

            <DataGridTextColumn.Binding>

                <MultiBinding Converter="{StaticResource indexConverter}">

                    <Binding />

                    <Binding RelativeSource=

                                "{RelativeSource AncestorType={x:Type DataGrid}}"

                            Path="ItemsSource"></Binding>

                </MultiBinding>

            </DataGridTextColumn.Binding>

        </DataGridTextColumn>

        <DataGridTextColumn Header="Name"

                           Binding="{Binding Name}"></DataGridTextColumn>

        <DataGridTextColumn Header="Age"

                           Binding="{Binding Age}"></DataGridTextColumn>

    </DataGrid.Columns>

 </DataGrid> 

Here is the IndexConverter:

    class IndexConverter : IMultiValueConverter

    {

        public object Convert(

            object[] values, Type targetType,

            object parameter, System.Globalization.CultureInfo culture)

        {

            var customer = values[0] as Customer;

            var customers = values[1] as List<Customer>;

            return customers.FindIndex(c => c == customer).ToString();

        }

 

        public object[] ConvertBack(

            object value, Type[] targetTypes,

            object parameter, System.Globalization.CultureInfo culture)

        {

            throw new NotImplementedException();

        }

    }

and finally, the binding operation in the Loaded event:

        void MainWindow_Loaded(object sender, RoutedEventArgs e)

        {

            dg.ItemsSource = DataAccess.Customers;

        }

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

3 תגובות

  1. Tamir Khason17 באפריל 2010 ב 22:21

    Well, if you really want to show the index of the item in view, do it within your view








    public class Conv : IValueConverter {

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
    var item = (ListViewItem)value;
    var view = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
    int index = view.ItemContainerGenerator.IndexFromContainer(item);
    return index;
    }

    הגב
  2. David Sackstein24 באפריל 2010 ב 21:25

    Hi Tamir,
    Thanks for the comment.
    But, wouldnt you agree that your solution ties the implementation tightly with the view (ListViewItem for instance).
    If I changed the view I would have to change the converter.
    David

    הגב
  3. Michael19 בפברואר 2011 ב 4:32

    I have tried Tamir Khason's approach, but

    always set the column-width to 0.

    Is it possible to make the column automatically fit to the width (like it does with _Binding Path=FirstName_), when using Binding Converter and RelativeSource?

    הגב