我不懂 C#,我正在从例子中、从我的错误和失败中慢慢学习。))。我观察其他人是如何做的,并将代码片段应用到我的项目中。今天我制作了一个应用程序(使用 MVVM),其窗口分为两部分,左侧部分(侧边栏)有一个菜单,右侧部分有来自不同用户控件的内容。将 SQLite 数据库与 EF 连接并迁移到项目。数据输入到数据库并显示在 DataGrid 中。但卡在删除上,或者更确切地说,卡在删除已删除行的显示上。那些。在数据库中,该行已被删除,但 DataGrid 并未发生改变。我将尽我所能描述现在的情况:有一个带有 DataGrid 的 UserControl,并且显示的每行都有一个按钮。以下是 Reports.xaml 文件的 View 文件夹中的一些代码
<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 然后返回,则变化是可见的。我认为这里有一些基本而简单的东西,我已经尝试了第三天来寻找它)模型中的数据模型
public class ReportModel
{
[Key]
public int id_report { get; set; }
public string? name_report { get; set; }
public string? date_report { get; set; }
}
在 ViewModel 中的 @EvgeniyZ 示例中,我更正了一行并删除了一行,注释行就是那里的内容
现在一切正常,按下 DB 中的按钮即可删除行,并在 DataGrid 中删除该行。
这不是答案,只是对代码的一些评论,我认为了解这些评论会很有用。
DataContext
在 XAML 中这很糟糕(为什么)。DataContext
控件,因为它们默认为父上下文,这在 90% 的情况下就足够了。如果您设置了控件的上下文,那么数据源就会出现问题,因为在正常项目中,在创建主窗口时设置一次上下文就足够了。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 的重要逻辑。p
?它是什么cnv1
?现在您知道了,但是几年后您就能马上知道吗?不。因此,不要吝惜字母,立即给出正常且可理解的名称。好吧,此时我还要注意名字的重复。您的课程被调用ReportModel
,在那里可以立即清楚它是什么Report
。为什么房产证上到处都是_report
?Report.Report
一些...