gpt4 book ai didi

wpf - mvvm 中 crud 逻辑应该在哪里实现?

转载 作者:行者123 更新时间:2023-12-03 10:28:58 24 4
gpt4 key购买 nike

在我的 MVVM Light 应用程序中,我在客户列表中进行了搜索。搜索缩小了显示在主/明细 View 中的客户列表,其中包含一个数据网格(主 CustomerSearchResultView)和一个单独定义的具有名字、姓氏、地址等的用户控件(详细信息 - CustomerSearchDetailView)。以下是主/明细 View 的主要内容:

 <StackPanel MinWidth="150" >
<TextBlock Text="Customer Search Result List" />
<Grid>
<DataGrid Name="CustomerList" ItemsSource="{Binding SearchResult}" SelectedItem="{Binding SelectedRow, Mode=TwoWay}" >
.....
</DataGrid>
</Grid>
<Grid Grid.Column="2">
<TextBlock Text="Customer Details" Style="{StaticResource Heading2}" Margin="30,-23,0,0"/>
<content:CustomerSearchDetail DataContext="{Binding SelectedRow}" />
</Grid>
</Grid>
</StackPanel>

两者都有对应的 ViewModel。请备注 CustomerSearchDetail, SelectedRow 的 DC - 它是 CustomerSearchResultViewModel 上的一个属性,定义如下:
private Customer _selectedRow;
...
public Customer SelectedRow
{
get { return _selectedRow; }
set
{
_selectedRow = value;
RaisePropertyChanged("SelectedRow");
}
}
...

因此,我没有在 CustomerSearchDetailView 上定义任何 DC - 它是在“主” View 的绑定(bind)中设置的(如上所示)并且它似乎工作正常。

在我的 Model 文件夹中,我创建了这里使用的 Customer 类。它实现 ObservableObject 和 IDataErrorInfo 并具有引发propertychanged 事件的公共(public)属性。

我运行该应用程序,一切似乎都没问题。注意:CustomerSearchDetailView(即CustomerSearchDetailViewModel.cs)的ViewModel在这个阶段只是一个空 shell 并且没有被使用(据我所知......构造函数从未被访问过)

现在我想在详细 View 中向我的客户添加保存/更新功能。好的,我向 CustomerSearchDetailView 添加一个 Save 按钮,如下所示:
<Button Content="Save" Command="{Binding Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

我在我的 CustomerSearchDetailViewModel 中创建了我的“SaveCommand”RelayCommand 属性 - 但它从未被访问过。

嗯……在谷歌上来回搜索之后,我想出了这个:
 <Button Content="Save" Command="{Binding Source={StaticResource MyCustDetails}, Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

我在此 View 中将“MyCustDetails”定义为指向 CustomerSearchDetailViewModel 的资源。瞧!我现在在调试时点击了该方法……但是,唉,我的客户当然是“空”的。 (实际上,我在这里花了 2 个小时实现 CommandParameter 并将其绑定(bind)到主 View 上的“SelectedRow”属性 - 但客户仍然是“null”)。

更多的谷歌搜索和搜索 mvvm 示例,我在 Customer 类(模型对象)上实现了我的“SaveCommand”。你猜怎么着?编辑过的客户被传递了 - 我可以将它发送到我的 EF 层,一切似乎都没问题......

而且 - 如果你还在我身边 - 我的问题来了:

1.) 我希望 - 并且认为这是做事的“正确的 MVVM 方式” - 让我的 CRUD/Repository 在 ViewModel 中访问。我怎么能在我的场景中做到这一点?

2.) 现在我已经通过模型类(客户)设置了我的 CRUD - 我应该为问题 1 烦恼吗?事实上,我已经删除了 CustomerSearchDetailViewModel 并且一切运行正常。我觉得我已经发明了 View - 模型(MV)框架...... :-P

我非常希望对此提供反馈 - 我为这个“文字墙”道歉。

最佳答案

假设 DC 意味着 DataContext
只是我的观点:

  • 第一个问题是你对 SelectedRow 做了什么特别的事情吗?在 CustomerSearchResultViewModel ?

  • 如果答案是否定的,只需摆脱该属性(property)并拥有您的 CustomSearchDetailView直接绑定(bind)到 DataGrid使用 {Binding ElementName=CustomerList, Path=SelectedItem}
  • 现在您的保存/更新命令需要被 Button 使用在 CustomerSearchDetailView .因此,我立即倾向于为该 View 使用单独的 VM,并在那里定义这些命令。

  • 现在您提到未访问这些命令。那么答案是因为在你的程序中你从来没有真正创建过 CustomerSearchDetailViewModel .

    正常操作是你的View的 DataContext它是 VM(如果它需要一个。在你的情况下你做 imo cos 你需要它来保存你的命令)

    查看您的代码,我猜您使用的是 MVVM Light。所以在 ViewModelLocator你有你的 Main属性,在您的主 View 中,您得到了 DataContext使用该设置 Main属性(property)和 Source={StaticResource Locator}其中 Locator 是 ViewModelLocator在 App.xaml 资源中创建。因此,这将为定义该 DataContext 的 View 创建该 ViewModel。你当然可以在代码隐藏中做同样的事情,但我们不要偏离主题。

    因此,在您的情况下,您将 DataContext 设置为 SelectedRow类型为 Customer和绑定(bind)使用 DataContext 解析这就是为什么当您的命令在 Customer 中定义时它工作正常,但是当它在 VM 中时却没有。

    那么为什么当您在 VM 中使用命令并使用它时它会起作用
    <Button Content="Save" Command="{Binding Source={StaticResource MyCustDetails}, Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

    ^^ 这很有效,因为 DataContextSource 起未使用已经明确规定。随时随地 MyCustDetails在资源中定义,在那里创建了 VM。

    那么它的工作有什么问题呢?

    嗯,这是一个相当大的困惑。也就像你提到的 Customer该 VM 中的详细信息为空。好吧,我希望你现在能猜到为什么会这样。这是因为您的 VM 是通过 x:Key="MyCustDetails" 在资源中创建的但除了绑定(bind)明确引用它时,它没有任何东西被使用或分开

    在这个系统中,我们得到的命令要么是指完全错误的模型,要么是为此目的而作为资源创建的虚拟机。 DataContext与“SearchResults” View 密切相关,这使得 future 的扩展或布局更新变得不那么容易。

    如果我们保持 View <-> VM 为 1 <-> 1 的关系,我们就可以避免所有这些混淆。总而言之,我们可以一起回答您的两个问题。虽然这有效,但请不要让您的代码像这样并调整它以更好地帮助 future 扩展并遵守一些基本准则。

    那么我们该怎么做呢?

    方法一:
  • 在您的 CustomerSearchDetail查看、添加 DependencyProperty类型 Customer让我们称之为 SelectedCustomer .
  • 现在更换 DataContext="{Binding SelectedRow}"SelectedCustomer="{Binding SelectedRow}"CustomerSearchResultView
  • 现在设置您的 CustomerSerachDetailView 的 DataContext因为它的 VM 类似于 CustomerSerachResultsView链接到它的 VM(使用 ViewModelLocator 通过 xaml 中的 DataContext 绑定(bind)猜测)
  • 现在您可以在 Button 中使用您的命令的 CustomerSerachDetailView正如 <Button Command="{Binding SaveCommand}" ...
  • 最后因为SelectedRow不再是 DataContextCustomerSerachDetailsView ,您的名字、姓氏、地址的绑定(bind)都将停止工作。

  • 我们有很多选择来解决这个问题。

    首先是在每个 Binding 中使用一个指向 CustomerSerachDetailsView 的 RelativeSource FindAncestor 绑定(bind)。然后通过我们在获取适当字段之前创建的 CurrentCustomer DP( DependencyProperty )。

    例如:
    <TextBlock Text={Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CustomerDetailsView}}, Path=CurrentCustomer.FirstName}" />

    现在如果你有多个属性,这很快就会开始变得烦人。所以然后选择一个共同的祖先(比如这些 TextBlock 中的 3 个被分组在 StackPanel 下)并将它的 DataContext 应用为 CurrentCustomer元素通过与 ^^ ​​类似的绑定(bind)。现在 StackPanel的子 DataContext 将是 Customer 元素,因此在它们的每个绑定(bind)中,您不必执行整个 RelativeSource 操作,只需提及 {Binding Path=FirstName}等等。

    就是这样。现在你有两个 View ,它们有各自的 VM 和模型( Customer ),每个 View 都有各自的任务。

    太好了,我们完成了吗?还没有完全出错。

    虽然方法 1 比我们开始时更好,但它仍然只是“meh”。我们可以做得更好。

    方法二

    MVVMLight 有一个 Messenger允许您以弱依赖格式在不同类之间进行通信的类。如果你还没有,你需要研究一下。

    那么我们如何处理 Messenger ?

    很简单:
  • SelectedRow 的 setter 中在 CustomerSearchResultsViewModel我们将发送带有新传入的消息 valueCustomerSearchDetailsViewModel .
  • 现在在 CustomerSearchResultsViewModel我们将添加一个属性 CurrentCustomer并为其分配此传入值。
  • CustomerSerachDetailsView我们不再创建DP。这意味着我们不再设置 SelectedRowCustomerSerachDetailsView 中的任何内容(DataContext 或 DP)来自 CustomerSearchResultsView (甜少工作:))
  • 至于我们分配的方式DataContextCustomerSerachDetailsView或者我们绑定(bind)Button.Command的方式- 它们与方法 1 相同
  • 最后是实际的“名字”等绑定(bind)。现在好了 CurrentCustomerCustomerSearchDetailsViewModel 的属性.所以绑定(bind)到它就像 Button 绑定(bind)到它的命令一样


  • ^^ 现在可以正常工作了 cos DataContextTextBlock是 VM 和属性 CurrentCustomer存在于其中。

    关于wpf - mvvm 中 crud 逻辑应该在哪里实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17580845/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com