Reputation: 97
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?
<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>
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:
After the change (using ObservableCollection), the running DataGrid looked like this:
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" >
@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
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
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