ˈvɔlə
ˈvɔlə

Reputation: 10242

How to prevent Item to be added to DataGrid?

My Problem

I am trying to prevent the user to add empty DataGrid rows when using the built-in .NET DataGrid AddNewItem-functionality. So when a user tries to commit an AddNew transaction of the DataGrid and leaves PageItemViewModel.Text empty, it should disappears from the DataGrid.

The code

ViewModels

public class PageItemViewModel
{
    public string Text { get; set; }
}

public class PageViewModel
{
    public ObservableCollection<PageItemViewModel> PageItems { get; } = new ObservableCollection<PageItemViewModel>();
}

View

<DataGrid AutoGenerateColumns="True"
          CanUserAddRows="True"
          ItemsSource="{Binding PageItems}" />

I already tried

... removing the automatically created object from the DataGrid's ItemsSource while handling:

... but always receive exceptions like:

  • "System.InvalidOperationException: 'Removing' is not allowed during an AddNew or EditItem transaction."
  • "System.InvalidOperationException: Cannot change ObservableCollection during a CollectionChanged event."
  • "System.InvalidOperationException: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."

My question

How can I prevent the newly created PageItemViewModel from being added to the ObservableCollection<PageItemViewModel>, when there is a given condition (in this case: String.IsNullOrWhiteSpace(PageItemViewModel.Text) == true.


EDIT:

@picnic8: The AddingNewItem event does not provide any form of RoutedEventArgs and therefore no Handled property. Instead, it is AddingNewItemEventArgs. Your code is invalid.

private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e)
{
    var viewModel = (PageItemViewModel)e.NewItem;
    bool cancelAddingNewItem = String.IsNullOrWhiteSpace(viewModel.Text) == true;
    // ??? How can i actually stop it from here?
}

Upvotes: 4

Views: 894

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205619

You can't and shouldn't prevent adding to the underlying collection because when the end user starts typing in the new row, DataGrid will create and add a new PageItemViewModel object which at that time is initialized with default values.

What you can do though is to prevent committing that object by handling the DataGrid.RowEditEnding event when DataGridRowEditEndingEventArgs.EditAction is DataGridEditAction.Commit and use DataGrid.CancelEdit method to effectively remove the new object (or restore the existing object state) when the validation failed.

private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
    if (e.EditAction == DataGridEditAction.Commit)
    {
        var bindingGroup = e.Row.BindingGroup;
        if (bindingGroup != null && bindingGroup.CommitEdit())
        {
            var item = (PageItemViewModel)e.Row.Item;
            if (string.IsNullOrWhiteSpace(item.Text))
            {
                e.Cancel = true;
                ((DataGrid)sender).CancelEdit();
            }
        }
    }
}

An important detail is that the RowEditEnding event is fired before pushing the current editor value to the data source, so you need to do that by hand before performing the validation. I've used BindingGroup.CommitEdit method for that.

Upvotes: 6

Picnic8
Picnic8

Reputation: 373

In your VM, subscribe to the AddingNewItem event and check your condition there. You can stop the action if the condition fails.

var datagrid.AddingNewItem += HandleOnAddingNewItem;

public void HandleOnAddingNewItem(object sender, RoutedEventArgs e)
{
    if(myConditionIsTrue)
    {
        e.Handled = true; // This will stop the bubbling/tunneling of the event
    }
}

Upvotes: -1

Related Questions