C# wpf Как удалить строку в DataGrid?

Я не знаю с#, потихоньку изучаю по примерам, по своим ошибкам и неудачам)). Смотрю как другие делают и применяю куски кода к себе в проект. На сегодняшний день я сделал приложение (с использованием MVVM) окно которого поделено на две части , в левой части (сайдбар) находиться меню, в правой части Content из разных UserControl. Подключил к проекту БД на SQLite с EF и миграцией. Данные в БД заносятся и в DataGrid отображаются. Но застрял на удалении, точнее на удалении отображении удаленной строки. Т.е. в БД строка удаляется а DataGrid не меняется. Как смогу опишу, что есть сейчас: И так есть UserControl в нем DataGrid , в каждой выведенной строке есть Button. Вот часть кода в папке View файл Reports.xaml

<UserControl x:Class= ...>
   <UserControl.DataContext>
        <vm:ReportsVM/>
   </UserControl.DataContext> ...
...
<DataGrid  x:Name="ReportsDataGrid"
          ItemsSource = "{Binding Source={x:Static vm:ReportsVM.reportsView}}" 
          SelectedCellsChanged="NO_SelectedCellsChanged"> ....
 ...
 <Button  Name="BtnReportRemove"  
            Content="Удалить отчет"
            Tag="{Binding id_report}"
            CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}"
            Command="{Binding Path=DataContext.DeleteReportCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
... 

Данные для DataGrid беру из БД , есть вот такой класс:

public static List<ReportModel> GetAllReports()
{
    using (var db = new ContextModel())
    {
        return db.dbReportTable.OrderBy(b => b.id_report).ToList();
    }
}

и загоняю их в коллекцию, во ViewModel есть следующий код:

 private ObservableCollection<ReportModel> reports;
 public ObservableCollection<ReportModel> Reports
 { 
     get { return reports; }
     set { Set(ref reports, value); }
 }
public static CollectionViewSource reportsView = new CollectionViewSource();
...

...
public ReportsVM()
{
    DeleteReportCommand = new LambdaCommand(OnDeleteReportExecuted, CanDeleteReportExecute);

    Reports = new ObservableCollection<ReportModel>(ReportData.GetAllReports());
    reportsView = new CollectionViewSource();
    reportsView.Source = Reports;
}

Так как к кнопке я привязал id_report Tag="{Binding id_report}" то во ViewModel в команде удаляю из БД строку с данным id_report. Код команды удаления во ViewModel

// Команда удаления строки из БД и DataGrid
public ICommand DeleteReportCommand { get; set; }
private bool CanDeleteReportExecute(object p) => true;
private void OnDeleteReportExecuted(object p)
{    
    int cnv1;
    cnv1 = Convert.ToInt32(p);
    MessageBoxResult result = MessageBox.Show("Вы действительно хотите удалить Отчет?", "Удаление", MessageBoxButton.YesNo, MessageBoxImage.Question);
    if ( result == MessageBoxResult.No) return;
    using (ContextModel db = new ContextModel())
    {
        var orderInDb = db.dbReportTable.First(x => x.id_report == cnv1);
        db.dbReportTable.Remove(orderInDb);
        db.SaveChanges();
    }
    
    var collection = reports;
    var item = collection.Where(X => X.id_report == cnv1).FirstOrDefault();
    int index = collection.IndexOf(item);
    reports.RemoveAt(index);
    //Reports.Clear();
    //Reports = new ObservableCollection<ReportModel>(ReportData.GetAllReports());
    //((ObservableCollection<ReportModel>)reportsView.Source).Clear(); 
}

Закомментированные строки это я пытался сделать, но ничего не получается. Т.е. из БД строка удаляется, из коллекции строка удаляется (по точке останова видно отсутствие в коллекции строки с выбранным индексом) , а вот в DataGrid ни каких изменений, и если только я перейду из этого UserControl в другой UserControl и потом вернуcь, то изменение видны. Я думаю тут что-то элементарное и простое, а я уже третий день пытаюсь найти это)) Модель данных в Model

public class ReportModel 
{
    [Key]
    public int id_report { get; set; }

    public string? name_report { get; set; }
    public string? date_report { get; set; }
}

Ответы (2 шт):

Автор решения: Rustam

При примеру @EvgeniyZ во ViewModel поправил одну строку и одну убрал , закомментированные строки это то, что было

 private ObservableCollection<ReportModel> reports;
 public ObservableCollection<ReportModel> Reports
 { 
     get { return reports; }
     set { Set(ref reports, value); }
 }
  public static CollectionViewSource reportsView = new();
//public static CollectionViewSource reportsView = new CollectionViewSource();
...

...
public ReportsVM()
{
    DeleteReportCommand = new LambdaCommand(OnDeleteReportExecuted, CanDeleteReportExecute);

    Reports = new ObservableCollection<ReportModel>(ReportData.GetAllReports());
    //reportsView = new CollectionViewSource();
    reportsView.Source = Reports;
}

И теперь все заработало, строка удаляется по нажатию на Button из БД и удаляется в DataGrid.

→ Ссылка
Автор решения: EvgeniyZ

Не ответ, чисто замечания по коду, которые думаю, будет полезно знать.

  1. DataContext в XAML плохо (почему).
  2. Вы не должны хотеть задавать DataContext контролам, ибо они по умолчанию берут контекст родителя, чего в 90% случаев достаточно. Если вы задаете контекст контролам, то значит у что-то не так с источниками данных, ибо в обычном проекте достаточно 1 раза установить контекст самому главному окну при его создании.
  3. x:Name быть не должно, лишь только если вы хотите через XAML обратиться к нужному контролу (стили/триггеры), но не через c# код!
  4. Tag - это костыль... Да и весьма странный, ибо вы сначала привязываете Tag, затем ищите этот тег и передаете его в CommandParameter, и вот что мешает сделать сразу? Ну а костыль заключается в том, что тег - это значит то, что что-то пошло в проектировании не так, и вы вынуждены в UI коде хранить важные данные.
  5. Привязка через x:Static - это еще один костыль, которым вы непонятно что решаете. У вас НЕ ДОЛЖНО быть в коде статичных объектов, а все привязки ДОЛЖНЫ быть к публичным СВОЙСТВАМ. Вы же непонятно зачем сделали статичное поле, которое через статику ищите и пытаетесь к нему привязаться. Это не правильно, это плохо. Если вам надо передать данные из класса в класс, то передайте ссылку на нужный класс и возьмите от туда нужное свойство, а не делайте там некий статичным объект, который постоянно висит в память.
  6. public static - опять статика, когда как это конкретный метод, конкретного объекта.
  7. new ContextModel() - EF довольно умный, чтобы закрывать за собой контексты самостоятельно, вам достаточно его один раз создать и хранить как экземпляр класса, который будет жить все время жизни класса, а не повторно пересоздаваь при каждом вызове и постоянно выгружать.
  8. .ToList(); - Очередной костыль. Вызвав у EF .To..() вы выгружаете из базы ВСЕ данные в память, которых там может быть тонна, тем самым вы забьете память клиента лишним мусором. Так делать не стоит, либо очень осторожно.
  9. Также метод получения данных из базы стоит делать асинхронным, советую подумать в этом направлении.
  10. static CollectionViewSource - статика (но об этом написал), ну и сам CollectionViewSource вам не нужен в данном коде. Он нужен тогда, когда вы на уровне UI хотите отфильтровать/сгруппировать элементы, чего в вашем коде я не вижу, но может просто не показали...
  11. Довольно топорная реализация ICommand, советую поискать что-то получше, ибо сейчас вы пишете много лишнего и много лишних преобразований. А вообще, советую библиотеку CommunityToolkit.MVVM, где весь ваш код заменится 1-2 строками кода.
  12. Convert.ToInt32 - любой базовый тип имеет метод Parse или TryParse, стоит использовать их, а не старый конвертор. 13. MessageBox - это View слой, а по правилам MVVM VM слой не должен ничего знать про View и наоборот. Другими словами, вы не должны в VM слое использовать UI компоненты, включая MessageBox.
  13. .Where(X => X.id_report == cnv1).FirstOrDefault(); - Where лишний, достаточно просто .FirstOrDefault(x => x.id_report == cnv1).
  14. var collection = reports; - не понятно зачем вы храните ссылку на коллекцию, когда как можете сразу обращаться сразу к нужному.
  15. reports и Reports - это две разные штуки, где в первом случае идет публичное поле без особой логики, а во втором публичное свойство, у которого в set есть важная логика обновления UI.
  16. Именования. В C# принято писать переменные следуя строго конкретным правилам, а именно CamelCase, слитно, без подчеркиваний, где каждое слово с заглавной буквы. Приватные поля начинаются с маленькой буквы. Это стандарт, которому все придерживаются. Также давайте более осмысленные имена, чтобы код был понятным. Вот что такое p? А что такое cnv1? Сейчас вы знаете, а через пару лет сможете сразу сказать? Нет. Поэтому не экономьте буквы, давайте нормальные и понятные имена сразу. Ну в этом пункте еще замечу про дублирование имен. У вас класс зовется ReportModel, где сразу понятно, что это Report. Зачем в названии свойств везде _report? Report.Report какой-то...
→ Ссылка