Indinfer
Indinfer

Reputation: 97

DataGrid bound to List not letting user add new row

I know that this question is duplicate of several others because I have been studying those "several others." And I have been unable to figure out where I'm messing up.

This post is specifically about:

When I run my code, DataGrid allows me to change and delete rows. DataGrid does not allow me to add a row. Why?

Xaml:

<Window
    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:myNamespace"
    xmlns:Schema="clr-namespace:System.Xml.Schema;assembly=System.Xml.ReaderWriter" x:Class="myNamespace.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="600">
<Grid>
    <DataGrid x:Name="dataGrid" AutoGenerateColumns="False"   >
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="col_id" Width="200" Header="Col ID"  Binding="{Binding value_id}" />
            <DataGridTextColumn x:Name="col_1" Width="200" Header="Col One"  Binding="{Binding value_1}" />
            <DataGridTextColumn x:Name="col_2" Width="200" Header="Col Two"  Binding="{Binding value_2}" />
        </DataGrid.Columns>
    </DataGrid>

</Grid>

Code Behind:

using System.Collections.Generic;
using System.Windows;

namespace myNamespace
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            init_item_list();
            dataGrid.ItemsSource = ItemList;
        }


        public List<DataItem> ItemList = new List<DataItem>();

        public void init_item_list()
        {
            ItemList.Add(new DataItem(1, "one", "i"));
            ItemList.Add(new DataItem(2, "two", "ii"));
            ItemList.Add(new DataItem(3, "three", "ii"));
        }
        public class DataItem
        {
            public DataItem(int id, string val_1, string val_2)
            {
                value_id = id;
                value_1 = val_1;
                value_2 = val_2;
            }
            private int _value_id;
            public int value_id
            {
                get { return _value_id; }
                set { _value_id = value; }
            }

            private string _value_1;
            public string value_1
            {
                get { return _value_1; }
                set { _value_1 = value; }
            }

            private string _value_2;
            public string value_2
            {
                get { return _value_2; }
                set { _value_2 = value; }
            }


        }

    }
}

I believe I saw rows added from the user interface when DataGrid is bound to a List. However, I don't see it working with my code here.

Efraim Newman commented that could be the problem is using List. I have seen mention in other posts that ObservableCollection works better than List for DataGrid binding.

In the code behind, I changed

            public List<DataItem> ItemList = new List<DataItem>();

to

public ObservableCollection<DataItem> ItemList = new ObservableCollection<DataItem>();

Before the change (using List), the running DataGrid looked like this:

Before

After the change (using ObservableCollection), the running DataGrid looked like this:

enter image description here

In either case, there is no blank row by which to add a new record.

I do not see any difference using ObservableCollection instead of List. DataGrid still does not allow me to add a record.

I think the advantage of using ObservableCollection is that ObservableCollection notifies (causes DataGrid) to update whenever there is a change in ObservableCollection. That does not seem to be the problem here.

Here, the problem is that DataGrid is not accepting any new entries.

In Xaml I tried explicitly setting CanUserAddRows to True and even to False.

    <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" CanUserAddRows="True"   >

The Answer

@mm8 found my problem: I needed to add a default, parameterless constructor. I added:

            public DataItem() { }

The Xaml remains as it was originally. Note I am now binding to List.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;

namespace myNamespace
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
    InitializeComponent();

    init_item_list();
    dataGrid.ItemsSource = _itemList;
    }


    private List<DataItem> _itemList = new List<DataItem>();
    public List<DataItem> ItemList
    {
    get { return _itemList; }
    set { _itemList = value; }
    }

    public void init_item_list()
    {
    _itemList.Add(new DataItem(1, "one", "i"));
    _itemList.Add(new DataItem(2, "two", "ii"));
    _itemList.Add(new DataItem(3, "three", "ii"));
    }
    public class DataItem
    {
    public DataItem() { }
    public DataItem(int id, string val_1, string val_2)
    {
        value_id = id;
        value_1 = val_1;
        value_2 = val_2;
    }
    private int _value_id;
    public int value_id
    {
        get { return _value_id; }
        set { _value_id = value; }
    }

    private string _value_1;
    public string value_1
    {
        get { return _value_1; }
        set { _value_1 = value; }
    }

    private string _value_2;
    public string value_2
    {
        get { return _value_2; }
        set { _value_2 = value; }
    }

}

I think an example where you cannot use List is where you will change List directly and need DataGrid to show the changes.

I have tested this code using both List and ObservableCollection. It now works from adding the default, parameterless constructor.

Upvotes: 0

Views: 124

Answers (2)

mm8
mm8

Reputation: 169220

A List<T> doesn't implement the INotifyCollectionChanged interface that is required for the view to be able to subscribe to items being added or removed and refresh the control automatically.

The only built-in class that implements this interface is ObservableCollection<T>. If you change the type of ItemList, it should work as expected:

public ObservableCollection<DataItem> ItemList = new ObservableCollection<DataItem>();

You should also consider either making ItemList a public property or rename it to _itemList and make it a private field:

private readonly ObservableCollection<DataItem> _itemList = new ObservableCollection<DataItem>();

For the DataGrid to display a blank row that lets you add an item to the source collection, your DataItem class must define a default parameterless constructor:

public DataItem() { }

Upvotes: 3

Efraim Newman
Efraim Newman

Reputation: 1041

It seems that the problem is that you are using List which doesn't tell WPF when it gets updated. you should use ObservableCollection instead.

Upvotes: 1

Related Questions