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

时间:2013-07-10 21:03:53

标签: wpf mvvm crud

在我的MVVM Light应用程序中,我在客户列表中进行搜索。搜索会缩小客户列表,这些列表显示在主/详细视图中,其中包含datagrid(主CustomerSearchResultView)和带有FirstName,Lastname,Address等的单独定义的usercontrol(详细信息 - CustomerSearchDetailView)。以下是主/详细视图的主要内容:

 <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的DC,SelectedRow - 它是CustomerSearchResultViewModel上的一个属性,定义如下:

private Customer _selectedRow;
...
public Customer SelectedRow
        {
            get { return _selectedRow; }
            set
            {
                _selectedRow = value;
                RaisePropertyChanged("SelectedRow");
            }
        }
...

因为这个我没有在CustomerSearchDetailView上定义任何DC - 它是在&#34; Master&#34;上的Binding中设置的。查看(如上所示),似乎工作正常。

在我的Model文件夹中,我创建了正在使用的Customer类。它实现了ObservableObject和IDataErrorInfo,并具有raisepropertychanged事件的公共属性。

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

现在,我想在详细信息视图中向我的客户添加“保存/更新”功能。好的,我向CustomerSearchDetailView添加了一个Save按钮,如下所示:

<Button Content="Save" Command="{Binding Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

我创建了我的&#34; SaveCommand&#34;我的CustomerSearchDetailViewModel中的RelayCommand属性 - 但永远不会被访问。

嗯......好吧,经过一些谷歌来回搜索,我想出了这个:

 <Button Content="Save" Command="{Binding Source={StaticResource MyCustDetails}, Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

我定义了&#34; MyCustDetails&#34;作为此视图中指向CustomerSearchDetailViewModel的资源。瞧!我现在在调试时遇到了这个方法...但是,我的客户当然是#34; null&#34;。 (事实上​​,我花了2个小时在这里实现CommandParameter并将其绑定到主视图上的&#34; SelectedRow&#34;属性 - 但客户仍然是&#34; null&#34;)。

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

并且 - 如果你还在我身边 - 我的问题就出现了:

1。)我想 - 并且认为这是适当的MVVM方式&#34;做事 - 让我的CRUD / Repository在ViewModel中访问。我怎么能在我的场景中做到这一点?

2。)现在我已经通过Model类(Customer)安装了我的CRUD - 我应该打扰问题1吗?实际上我删除了CustomerSearchDetailViewModel,一切运行正常。我觉得我发明了View - Model(MV)框架...... :-P

我非常希望得到关于此的反馈 - 我为这个&#34;文本墙&#34;道歉。

1 个答案:

答案 0 :(得分:2)

假设DC意味着DataContext

我的意见:

  • 第一个问题是你在SelectedRow中对CustomerSearchResultViewModel做了哪些特别的事情?

如果答案是否定的,只需删除该属性,并使用CustomSearchDetailViewDataGrid绑定到{Binding ElementName=CustomerList, Path=SelectedItem}

  • 现在,Button中的CustomerSearchDetailView需要使用您的保存/更新命令。所以我立即倾向于为该视图使用单独的VM并在那里定义这些命令。

现在您提到这些命令未被访问。那么答案就是因为在你的程序中你永远不会真正创建CustomerSearchDetailViewModel

正常操作是您的查看DataContext是它的VM(如果它需要一个。在您的情况下,您需要它来保存您的命令)

查看你的代码我猜你使用MVVM Light。因此,在ViewModelLocator中,您拥有Main属性,并且在主视图中,您使用DataContext属性设置了Main,并使用Source={StaticResource Locator}设置了定位符ViewModelLocator {1}}在App.xaml Resources中创建。这样就为该视图创建了定义该DataContext的ViewModel。你可以在代码隐藏中做同样的事情但是不要偏离主题。

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

那么为什么在VM中使用命令并使用

时它才起作用
<Button Content="Save" Command="{Binding Source={StaticResource MyCustDetails}, Path = SaveCommand}" Width="80" Margin="0,0,15,0"/>

^^之所以有效,是因为DataContext未被使用,因为Source已明确指定。在资源中定义了MyCustDetails,在那里创建了虚拟机。

所以它有什么用呢?

嗯,这太乱了。就像你提到的Customer那个VM中的细节是空的一样。好吧,我希望你能猜到为什么到现在为止。这是因为您的虚拟机是通过x:Key="MyCustDetails"在资源中创建的,但它中没有任何内容被使用或与Binding明确引用它时的区别

在这个系统中,我们得到的命令既可以引用明显错误的模型,也可以引用作为资源创建的VM。 DataContext与&#34; SearchResults&#34;密切相关。查看使未来的扩展或布局更新变得不那么容易。

如果我们保留View&lt; - &gt; VM a 1 - &lt; 1&gt;。 1关系我们可以避免所有这些混乱。总而言之,我们可以一起回答您的问题。虽然这有效,但请不要让您的代码像这样,并调整它以更好地帮助扩展以及遵守一些基本准则。

那我们该怎么做?

方法1:

  1. CustomerSearchDetail视图中,添加Customer类型SelectedCustomer,我们称之为DataContext="{Binding SelectedRow}"

  2. 现在将SelectedCustomer="{Binding SelectedRow}"替换为CustomerSearchResultView

  3. 中的CustomerSerachDetailView
  4. 现在将CustomerSerachResultsView的DataContext设置为与ViewModelLocator如何链接到其VM的方式类似(通过{x 1}使用{来猜测DataContext绑定{1}})

  5. 现在,您可以在Button CustomerSerachDetailView <Button Command="{Binding SaveCommand}" ...中使用SelectedRow

  6. 最后因为DataContext不再是CustomerSerachDetailsView的{​​{1}},所以您对FirstName,Lastname,Address的绑定似乎都会停止工作。

    < / LI>

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

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

    例如:

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

    现在,如果你有多个属性,这很快就会开始变得烦人的打字。然后选择一个共同的祖先(比如这些TextBlock中的3个被分组在StackPanel下)并通过与^^类似的绑定将其作为CurrentCustomer元素应用它。现在StackPanel的子DataContext将成为Customer元素,因此在他们的每个绑定中你不必完成整个RelativeSource的事情,只需提及{{1} } 等等。

    那就是它。现在你有了两个拥有各自VM和模型({Binding Path=FirstName})的视图,每个视图都有各自的任务。

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

    虽然方法1 比我们开始时更好,但仍然只是&#34; meh&#34;。我们可以做得更好。

    方法2

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

    那么我们如何处理Messenger

    非常简单:

    1. Messenger的{​​{1}}设置器中,我们会发送一条消息,其中包含新来的SelectedRowCustomerSearchResultsViewModel

    2. 现在value我们会添加一个属性CustomerSearchDetailsViewModel并为其分配此传入值。

    3. CustomerSearchResultsViewModel我们不再创建DP。这意味着我们不再将CurrentCustomer设置为来自CustomerSerachDetailsView的{​​{1}}中的任何内容(DataContext或DP)(甜蜜少工作:))

    4. 至于我们分配SelectedRow CustomerSerachDetailsView的方式或我们绑定CustomerSearchResultsView的方式 - 它们与方法1

    5. 最后实际的&#34; FirstName&#34;所以Binding's。那么现在DataContextCustomerSerachDetailsView的属性。所以绑定到它就像Button绑定它的命令

    6. ^^这个工作正常现在Button.Command的{​​{1}}为CurrentCustomer是虚拟机,其中存在属性CustomerSearchDetailsViewModel