Juan Valenzuela
Juan Valenzuela

Reputation: 33

WPF - New item row not being shown in DataGrid when not binding to the ItemsSource property

I'm trying to create a custom DataGrid based on the WPF's DataGrid control. I created another property called "ItemsDataSource" and use it to bind collections from my ViewModels. When this property raises the ValueChanged event, it sets the value of ItemsSource to the value of ItemsDataSource.

This works fine when the grid is in Read Only mode, but when I set the property CanUserAddRows to True, if the ItemsDataSource is empty, my DataGrid never shows the new line to add new rows. BUT, if I change the binding back to ItemsSource instead of my ItemsDataSource, the DataGrid shows the new line.

Here is the partial code for my custom Grid:

public partial class NewDataGrid : DataGrid
{
    public NewDataGrid()
    {
        InitializeComponent();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsDataSourceProperty, typeof(NewDataGrid));

        dpd?.AddValueChanged(this, (s, a) =>
        {
            ItemsSource = ItemsDataSource.Cast<object>().ToList();
        });
    }

    public IList ItemsDataSource
    {
        get { return (IList)GetValue(ItemsDataSourceProperty); }
        set { SetValue(ItemsDataSourceProperty, value); }
    }

     public static readonly DependencyProperty ItemsDataSourceProperty =
                DependencyProperty.Register("ItemsDataSource", typeof(IList), typeof(NewDataGrid), new PropertyMetadata(null));
} 

And here is how I'm doing the binding in XAML:

<WPF:NewDataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
      ItemsDataSource="{Binding Path=DataContext.DataWrapperList, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
      SelectedValue="{Binding Path=DataContext.SelectedDataWrapper, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
      AutoGenerateColumns="False"
      Validation.ErrorTemplate="{x:Null}"
      Margin="0"
      VerticalScrollBarVisibility="Auto"
      SelectionMode="Single"
      CanUserAddRows="True"
      CanUserDeleteRows="True"
      IsReadOnly="False">

    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="*" SortMemberPath="Name" />
        <DataGridTextColumn Header="Quantity" Binding="{Binding Path=Quantity}" Width="*" SortMemberPath="Quantity" />
</WPF:PAENewDataGrid>

Here is how the DataListWrapper property is declared in my DataContext:

public ObservableCollection<DataWrapper> DataWrapperList;

Here is my DataWrapper class:

public class DataWrapper : BaseWrapper
{
    private DataWrapperDTO _data;

    public DataWrapper()
    {
        _data = new DataWrapperDTO();
    }

    public DataWrapper(DataWrapperDTO data)
    {
        _data = data;
    }

    public string Name
    {
        get { return _data.Name; }
        set
        {
            _data.Name = value;
            RaisePropertyChanged(nameof(Name));
        }
    }

    public int Quantity
    {
        get { return _data.Quantity; }
        set
        {
            _data.Quantity = value;
            RaisePropertyChanged(nameof(Quantity));
        }
    }
}

Does anyone know how to force the DataGrid to always show this new line whenever the CanUserAddRows property is set to True?

Upvotes: 0

Views: 1039

Answers (2)

Juan Valenzuela
Juan Valenzuela

Reputation: 33

After struggling a little while with this problem, it seems that I've found a solution. Registering my DependencyProperty with a PropertyChangedCallback and assigning the ItemsSource to the new value inside this callback makes the grid add the blank line to add new values.

The NewDataGrid class code looks like this now:

public partial class NewDataGrid : DataGrid
{
    public NewDataGrid()
    {
        InitializeComponent();
        //Removed the DependencyPropertyDescriptor
    }

    public IList ItemsDataSource
    {
        get { return (IList)GetValue(ItemsDataSourceProperty); }
        set { SetValue(ItemsDataSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsDataSourceProperty =
            DependencyProperty.Register("ItemsDataSource", typeof(IList), typeof(NewDataGrid), new PropertyMetadata(null, ItemsDataSourceChanged));

    private static void ItemsDataSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var grid = sender as NewDataGrid;
        if (grid == null) return;

        grid.ItemsSource = ((IList)args.NewValue).Cast<object>().ToList();
    }
} 

Thanks for the help guys!

Upvotes: 0

ncfuncion
ncfuncion

Reputation: 227

Please try not to replace the ItemsSource every time a new item is added:

public NewDataGrid()
    {
        InitializeComponent();

        ItemsSource = new ObservableCollection<object>();

        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsDataSourceProperty, typeof(NewDataGrid));


        dpd?.AddValueChanged(this, (s, a) =>
        {
            ItemsSource.Clear();
            ItemsSource.Add(ItemsDataSource.Cast<object>().ToList());
        });
    }

Upvotes: 0

Related Questions