C# 了解如何在具有实体框架数据的MVVM中使用ICollectionView

C# 了解如何在具有实体框架数据的MVVM中使用ICollectionView,c#,wpf,mvvm,entity-framework-4,observablecollection,C#,Wpf,Mvvm,Entity Framework 4,Observablecollection,我目前有一个简单的应用程序,它由两个绑定到ObservableCollections的同步列表框组成,封装了两个不同的实体框架类 选择listBox1中的零件项时,它会将SelectedItem的导航键信息传递到listBox2,其中显示供应商实体的相应子集 当前,我的ViewModel(MainViewModel.cs)看起来像: public MainViewModel() { _context = new DBEntities(); _partsCollection = n

我目前有一个简单的应用程序,它由两个绑定到ObservableCollections的同步列表框组成,封装了两个不同的实体框架类

选择listBox1中的零件项时,它会将
SelectedItem
的导航键信息传递到listBox2,其中显示供应商实体的相应子集

当前,我的ViewModel(MainViewModel.cs)看起来像:

public MainViewModel()
{
    _context = new DBEntities();
    _partsCollection = new ObservableCollection<Part>(_context.Parts);
    _vendorsCollection = new ObservableCollection<Vendor>(_context.Vendors);
}

public ObservableCollection<Part> PartsCollection
{
     get { return _partsCollection; }
     set
     {
          OnPropertyChanged("PartsCollection");
          _partsCollection = value;
     }
}


public Observable<Part> SelectedPart
{
     get { return _selectedPart; }
     set
     {
          OnPropertyChanged("SelectedPart");
          _selectedPart = value;
     }
}

public ObservableCollection<Vendor> VendorsCollection
{
     get { return _vendorsCollection; }
     set
     {
          OnPropertyChanged("VendorsCollection");
          _vendorsCollection = value;
     }
}
    <UserControl.Resources>
    <local:MainViewModel x:Key="MainViewModelDataSource" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}">
           <ListBox ItemsSource="{Binding PartsCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  Margin="43,87,377,57" Name="listBox1"
             SelectedItem="{Binding SelectedPart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             IsSynchronizedWithCurrentItem="True" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding shapeName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <ListBox IsSynchronizedWithCurrentItem="True" 
             ItemsSource="{Binding ElementName=listbox2,  Path=SelectedItem.Vendors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Margin="345,87,75,57" 
             >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding mateStyle}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox> 
public主视图模型()
{
_context=新的DBEntities();
_partsCollection=新的ObservableCollection(_context.Parts);
_vendorsCollection=新的ObservableCollection(_context.Vendors);
}
公共可见收集部分收集
{
获取{return\u partsCollection;}
设置
{
不动产变更(“PartsCollection”);
_partsCollection=值;
}
}
公共可观察选择部分
{
获取{return\u selectedPart;}
设置
{
已更改的不动产(“选定部分”);
_selectedPart=值;
}
}
公共可观测集合供应商集合
{
获取{return\u vendorsCollection;}
设置
{
不动产变更(“卖方收款”);
_vendorsCollection=价值;
}
}
我的视图(MainView)如下所示:

public MainViewModel()
{
    _context = new DBEntities();
    _partsCollection = new ObservableCollection<Part>(_context.Parts);
    _vendorsCollection = new ObservableCollection<Vendor>(_context.Vendors);
}

public ObservableCollection<Part> PartsCollection
{
     get { return _partsCollection; }
     set
     {
          OnPropertyChanged("PartsCollection");
          _partsCollection = value;
     }
}


public Observable<Part> SelectedPart
{
     get { return _selectedPart; }
     set
     {
          OnPropertyChanged("SelectedPart");
          _selectedPart = value;
     }
}

public ObservableCollection<Vendor> VendorsCollection
{
     get { return _vendorsCollection; }
     set
     {
          OnPropertyChanged("VendorsCollection");
          _vendorsCollection = value;
     }
}
    <UserControl.Resources>
    <local:MainViewModel x:Key="MainViewModelDataSource" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}">
           <ListBox ItemsSource="{Binding PartsCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  Margin="43,87,377,57" Name="listBox1"
             SelectedItem="{Binding SelectedPart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             IsSynchronizedWithCurrentItem="True" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding shapeName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <ListBox IsSynchronizedWithCurrentItem="True" 
             ItemsSource="{Binding ElementName=listbox2,  Path=SelectedItem.Vendors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Margin="345,87,75,57" 
             >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock FontSize="13" Foreground="Black" Padding="3" Text="{Binding mateStyle}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox> 

这很有效。但是,我想将listBox2绑定到我的viewmodel的属性,以更新
VendorsCollection
。我想在我的viewmodel上使用“
SelectedPart
”属性,该属性目前还没有被使用

EF的全部要点是利用它作为ORM的所有功能,而不是在我的viewmodel中再次构建一个额外的ORM来发送更改通知。据我所见,设置一个
ICollectionView
是一个相当复杂的方法,但我无法理解绑定


我不确定下一步是什么,这样我就可以发出
PropertyChanged
通知并更新我的供应商
ObservableCollection
,将listBox2 xaml绑定到我的viewmodel的集合属性。

我也在使用MVVM和实体框架,但采用代码优先的方法。我已经厌倦了所有MVVM的“纯净”,其中ViewModel应该引发更改通知事件,所以我在我所有的模型类上实现了INPC,我以前从未如此高兴过!在ViewModels中复制所有模型属性只是为了通知更改,这太疯狂了

我看到您正在使用DBEntities,所以我不确定您将如何在模型对象中实现更改通知,但我强烈建议您这样做。我会让你的生活更轻松

顺便说一下,请确保在将backing字段设置为新值后引发OnPropertyChanged事件,如下所示:

 set
 {
      _selectedPart = value; //first you change the private backing field
      OnPropertyChanged("SelectedPart"); //then you notify that it has changed, so everything gets the new value
 }
关于绑定,您正在使用ViewModel的SelectedPart属性,如下所示:

SelectedItem="{Binding SelectedPart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsSynchronizedWithCurrentItem="True"
您正在将listbox1中的选定项绑定到SelectedPart属性,并且通过将IsSynchronizedWithCurrentItem设置为true,每次在UI中选择某个内容时,ViewModel中的属性都会更新(另一种方式是,如果在代码中设置SelectedPart,UI将接收更新)

因此,要在第二个列表框中绑定到SelectedPart的供应商,只需执行以下操作:

ItemsSource=“{Binding SelectedPart.Vendors,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}”

在这种情况下,您不需要创建VendorsCollection,因为您可以通过SelectedPart.Vendors访问相同的元素(或者您可以将VendorsCollection设置为具有良好名称的包装器,以返回SelectedPart.Vendor,只需确保选中SelectedPart为null)

关于ICollectionView,要知道它只是ObservaleCollection(或类似)的包装器,以便绑定到从选择器继承的框架元素(如ListBox)(也就是说,它们具有SelectedItem属性)

当您绑定到ObservableCollection时,WPF会在后台创建一个ICollectionView,这样您就可以获得SelectedItem。您可以创建自己的ICollectionView,将其与ObservableCollection一起提供,并以XAML的方式绑定到ICollectionView,就像使用ObservableCollection一样。如果您决定这样做,我建议您使用ListCollectionView,通过它可以过滤和创建组(用于TreeView)

最后一句话:请记住,ObservableCollection(以及所有ICollectionView)只会引发CollectionChanged事件(即,在集合中添加/删除项时)。如果更改属于集合的元素的属性,即使该元素实现了INotifyPropertyChanged,它们也不会引发任何问题。如果你想抓住这一点,你必须创建一个自定义集合,一个(多个)例子


希望这是有用的,谢谢

若要立即更改属性,只需在BindingList中传递ObservableCollection


EntityFramework在其System.Data.Entity命名空间中有一个ToBindingList扩展,可以帮助您实现这一点。

哇,谢谢,@Hannish。多亏了你,我无法告诉你我的星期一过得有多好。我感谢您的所有意见,并衷心同意您的MVVM方法。在我的实体框架模型中,我将实现一个单独的存储库,从中发送INPCs,并将EF objectset包装在ObservableCollections中(保存在我的模型文件夹中)。我最终将我的供应商对象集包装在一个
可观察集合中,并从PartsCollection setter发送了一个供应商属性更改通知。我还利用了您的评论“检查所选部分是否为空”,现在终于可以轻松地使我的代码像梦一样工作了,多亏了您。我不能谢谢你