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