0
votes

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?

2
Can you please provide the DataWrapperList property. Another thing i would check is to remove the Cast<object>().ToList() in ItemsSource = ItemsDataSource.Cast<object>().ToList();. I'm currently not in front of a pc with vs to check it, but i guess the ItemsSource only is a List<object> with one item in the list.Martin Backasch
@MartinBackasch I've made the changes you suggested, but it still not working :(Juan Valenzuela
what's wrong with binding directly to ItemsSource?ncfuncion
Contrary to your question text, DataListWrapper property is not a property but a field. It probably needs to be a property.grek40

2 Answers

0
votes

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());
        });
    }
0
votes

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!