'How to soft delete a selected row in a DataGrid View using a button

So I have a DataGridView called MyMovieDataDrid which is connected to a sql server, and I already have my IsDeleted property, and a delete repository which is shown below, by the way my IsDeleted property automatically shows up as a checkbox so if the checkbox is checked it's true if not it's false

public bool DeleteMovie(Guid id)
{
       bool isDeleted = false;
       var movie =  _context.Movie.FirstOrDefault(m => m.Id == id);
       if (movie != null)
       {
            movie.IsDeleted = true;
            _context.SaveChanges();
            isDeleted = true;
       
       } 
        return isDeleted;
}

and here is my Delete button method so when I press it, it runs my logic and soft deletes a row from the DataGridView I've tried multiple solutions like using the selected row event handler to get the selected rows then running the repository method but none have worked so far.

    private void DeleteButton_Click(object sender, EventArgs e)
    {
        Movie movie = new Movie();
        
        if(MyMovieDataGrid.SelectedRow.Count > 0)
        {
          _movieRepo.DeleteMovie(movie.Id);
        }
    }

and my all of my properties

    Movie movie = new Movie()
    {
        Id = Guid.NewGuid(),
        IsDeleted = false,
        MovieNames = MovieNameBox.Text;
     
    }
         

and my AddMovie repostitory

    public void AddMovie(Movie movie)
    {
        _context.Movie.Add(movie);
        _context.SaveChanges();

    }
      

Movie Repository Method

    private NRIDataContext _context;    
    public MovieRepository()
    {
        _context = new NRIDataContext();    
    }
       
     //GetMovie repository
     
   GetMovie() 
   {
            
    
    
        var movies = _context.Movie.Where(m => m.IsDeleted 
        ==false).ToList();
        return  movie;
                                                                                                                                                                                     
   }
      
     MyMovieDataGrid.DataSource = _movieRepo.GetMovie().OrderByDescending(x => x.MovieNames.First) .ToList();
  
     

so my question is how do I make my Datagrid know when to run my repository method and I feel like I have to somehow make write some code to where if the IsDeleted property is true it selects the whole row then I run my DeleteMovie Method but no solutions have worked.



Solution 1:[1]

I like Karen's approach for being more "how it should be done", but it's quite a far cry from what you've written and I suspect you might not want to change your current code massively

The basic problem with your approach is you don't get the movie id from the row that was clicked, you make a new movie which has a new random Guid which is supposedly guaranteed to not be in the database at all because Guids are very unlikely to repeat, and then you try and delete that:

private void DeleteButton_Click(object sender, EventArgs e)
{
    //make a new movie with random id 
    Movie movie = new Movie();
    
    if(MyMovieDataGrid)
    {
        //delete the new random id
        _movieRepo.DeleteMovie(movie.Id);
    }
}

this means that the odds of the movie you want deleting actually getting deleted are a staggering 5316911983139663491615228241121400000 to one; it probably won't happen if you start clicking now and keep going until the earth is engulfed by the sun going red giant ?

Retrieve the Guid you want to delete from the row clicked on; add a CellContentClicked handler and check it's the deleted button column being clicked and then pull the movie id out of the row's data bound item:

private void DGV_CellContentClick(object sender, 
DataGridViewCellEventArgs e)
{
    //don't process clicks on the header row 
    if(e.RowIndex < 0) return;

    //don't process clicks on columns other than delete
    if(e.ColumnIndex != MyMovieDataGrid.Columns["nameOfYourDeleteColumn"].ColumnIndex) return;

    
    //pull the movie from that row
    Movie movie = MyMovieDataGrid.Rows[e.RowIndex].DataBoundItem as Movie;
    
    if(MyMovieDataGrid) //I don't know what this means; a DataGridView is not a boolean
    {
        //delete the movie id
        _movieRepo.DeleteMovie(movie.Id);
    }
}

Solution 2:[2]

Consider setting up a filter in OnModelCreating in this case I'm using a model named Contact1 since I have this already. In the underlying table add the IsDeleted column but not in the model.

Below shows the basics to soft delete a row and remove it from the DataGridView. No proper repository.

public partial class Contact1 : INotifyPropertyChanged
{
    private int _contactId;
    private string _firstName;
    private string _lastName;

    public int ContactId
    {
        get => _contactId;
        set
        {
            _contactId = value;
            OnPropertyChanged();
        }
    }

    public string FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            OnPropertyChanged();
        }
    }

    public string LastName
    {
        get => _lastName;
        set
        {
            _lastName = value;
            OnPropertyChanged();
        }
    }

    public Contact1()
    {
        
    }

    public override string ToString() => $"{FirstName} {LastName}";

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

OnModelCreating

modelBuilder.Entity<Contact1>()
    .HasQueryFilter(contact =>
        EF.Property<bool>(contact, "isDeleted") == false);

Then override SaveChanges in the DbContext

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new ())
{
    DoShadowyStuff();
    return base.SaveChangesAsync(cancellationToken);
}

public override int SaveChanges()
{
    DoShadowyStuff();

    return base.SaveChanges();
}

Then in DoShadowlyStuff if the state is Deleted set the state to modified.

private void DoShadowyStuff()
{
    ChangeTracker.DetectChanges();

    foreach (var entry in ChangeTracker.Entries())
    {

        if (entry.State == EntityState.Deleted)
        {
            // Change state to modified and set delete flag
            entry.State = EntityState.Modified;
            entry.Property("isDeleted").CurrentValue = true;
        }
    }
}

Very basic read and delete operations

public class Operations
{
    public static void Remove(Contact1 contact1)
    {
        using (var context = new ShadowContext())
        {
            context.Add(contact1).State = EntityState.Deleted;
            context.SaveChanges();
        }
    }

    public static List<Contact1> Contacts()
    {
        using (var context = new ShadowContext())
        {
            return context.Contacts1.ToList();
        }
    }
}

Basic form operations. Click the delete button, set the state of the current row to deleted, save changes which marks the contact as modified and sets the isDeleted. Next remove the remove from the BindingList which in turn removes the row from the DataGridView.

public partial class Form1 : Form
{
    private BindingList<Contact1> _bindingList;
    private readonly BindingSource _bindingSource = new ();
    public Form1()
    {
        InitializeComponent();
        Shown += OnShown;
    }

    private void OnShown(object sender, EventArgs e)
    {
        _bindingList = new BindingList<Contact1>(Operations.Contacts());
        _bindingSource.DataSource = _bindingList;
        dataGridView1.DataSource = _bindingSource;
    }

    private void DeleteButton_Click(object sender, EventArgs e)
    {
        Operations.Remove(_bindingList[_bindingSource.Position]);
        _bindingList.RemoveAt(_bindingSource.Position);
    }
}

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Caius Jard
Solution 2 Karen Payne