2016-10-08 33 views
1

我在数据网格中遇到了大量的ComboBoxes问题。 真的想要一些帮助,我想我已经被研究的数量和我尝试过的东西弄糊涂了。这真的应该很简单,所以我必须错过一些东西。CollectionViewSource不用PropertyChanged更新

简化问题

我在XAML中使用CollectionViewSource,在C#中设置一类是页面的DataContext的是CollectionViewSource到一个ObservableCollection源。 将项目添加到集合不会更新包含显示视图源的DataGridComboBox列。 见线以下的详细


概述

我有一个WPF页面上有一个数据网格。 页面的数据上下文设置为视图模型。 viewModel包含两个可观察的集合。一个用于装备和一个用于地点。 每个装备都有一个位置。 这些从代码第一EF数据库填充,但我相信这个问题是高于该级别。

datagrid是每个设备一行。位置列需要是可供选择的组合框,允许用户更改位置。

我可以获取位置组合框的唯一方法是将其绑定到单独的集合视图源。

问题

看来,如果页面加载事件发生的视图模型前填充的ObservableCollection那么locationVwSrc将是空的,属性更改事件不会得到这个改变。

实现简短版本 页面有一个在xaml中定义的集合viewSource。

Loaded="Page_Loaded" 
    Title="EquipRegPage"> 
<Page.Resources> 
    <CollectionViewSource x:Key="locationsVwSrc"/> 
</Page.Resources> 

datagrid是用xaml定义的。

<DataGrid x:Name="equipsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" Margin="10,10,-118,59" 
       ItemsSource="{Binding Equips}" EnableRowVirtualization="True" AutoGenerateColumns="False"> 

在XAML定义的组合框柱

<DataGridComboBoxColumn x:Name="locationColumn" Width="Auto" MaxWidth="200" Header="Location" 
            ItemsSource="{Binding Source={StaticResource locationsVwSrc}, UpdateSourceTrigger=PropertyChanged}" 
            DisplayMemberPath="Name" 
            SelectedValueBinding="{Binding Location}" 

设置为视图模型

public partial class EquipRegPage : Page 
{ 
    EquipRegVm viewModel = new EquipRegVm(); 

    public EquipRegPage() 
    { 
     InitializeComponent(); 
     this.DataContext = viewModel; 
    } 

Loaded事件页面上下文设置上下文

private void Page_Loaded(object sender, RoutedEventArgs e) 
    { 
     // Locations View Source 
     System.Windows.Data.CollectionViewSource locationViewSource = 
      ((System.Windows.Data.CollectionViewSource)(this.FindResource("locationsVwSrc"))); 
     locationViewSource.Source = viewModel.Locations; 
     // Above does not work if the viewmodel populates these after this call, only works if its populated prior. 
     //TODO inotifypropertychanged not correct? This occurs before the viewmodels loads, and doesn't display. 
     // Therefore notify property changes aren't working. 

     // Using this as cheat instead instead works, i beleive due to this only setting the source when its full 
     //viewModel.Db.Locations.Load(); 
     //locationViewSource.Source = viewModel.Db.Locations.Local; 
     //locationViewSource.View.Refresh(); 
    } 

的ViewModel类以及它如何加载s

public class EquipRegVm : DbWrap, INotifyPropertyChanged 
{ 
    /// <summary> 
    /// Event triggered by changes to properties. This notifys the WPF UI above which then 
    /// makes a binding to the UI. 
    /// </summary> 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// <summary> 
    /// Notify Property Changed Event Trigger 
    /// </summary> 
    /// <param name="propertyName">Name of the property changed. Must match the binding path of the XAML.</param> 
    void RaisePropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 


    public ObservableCollection<Equip> Equips { get; set; } 

    public ObservableCollection<Location> Locations { get; set; } 

    public EquipRegVm() : base() 
    { 
     Load(); 
    } 

    /// <summary> 
    /// Load the data from the Model. 
    /// </summary> 
    public async void Load() //TODO async an issue? 
    { 
     // EQUIPMENT 
     ObservableCollection<Equip> eqList = new ObservableCollection<Equip>(); 
     var eqs = await (from eq in Db.Equips 
         orderby eq.Tag 
         select eq).ToListAsync(); 
     foreach(var eq in eqs) 
     { 
      eqList.Add(eq); 
     } 
     Equips = eqList; 
     RaisePropertyChanged("Equips"); 


     // LOCATIONS 
     ObservableCollection<Location> locList = new ObservableCollection<Location>(); 
     var locs = await (from l in Db.Locations 
         orderby l.Name 
         select l).ToListAsync(); 
     foreach (var l in locs) 
     { 
      locList.Add(l); 
     } 
     Locations = locList; 
     RaisePropertyChanged("Locations"); 
    } 
} 

回答

1

设置一个Binding象下面这样:

System.Windows.Data.CollectionViewSource locationViewSource = 
      ((System.Windows.Data.CollectionViewSource)(this.FindResource("locationsVwSrc"))); 
// locationViewSource.Source = viewModel.Locations; 

Binding b = new Binding("Locations"); 
b.Source = viewModel; 
b.Mode = BindingMode.OneWay; 
BindingOperations.SetBinding(locationViewSource, CollectionViewSource.SourceProperty, b); 

这是你所需要的。

+0

这当然解决了眼前的问题。谢谢@AnjumSKhan。李坎贝尔提出了一些好点,我当然有兴趣向他学习更多。 – Asvaldr

+0

是的,这将解决问题,但通过添加更多不必要的代码。 –

+0

我同意李,如果你有任何更多的见解我热衷学习。请参阅我对解决方案的最新评论。 – Asvaldr

2

看来你还没有能够把问题分解成足够小的问题。这个问题似乎是Datagrid中ComboBoxes的混合,异步设置CollectionViewSource源,从数据库加载数据。 我建议,这将是有益的或者考虑

  1. 具有最小重现问题(或soultion)移动部件即XAML文件和一个视图模型具有预罐装数据。

  2. 或解耦您现有的代码。看起来,Page明确知道你的ViewModel(EquipRegVm viewModel = new EquipRegVm();),你ViewModel明确地知道数据库以及如何加载它自己。哦,快点,现在我们的观点是耦合到您的数据库?这不就是像MVVM这样的模式,以至于我们没有耦合?

接下来我会看看一些代码,并看到更多(我会称之为)反模式。

  • 可设置集合属性后面
  • 代码的页面(所有能住在XAML)

但我认为基本上,如果你只是在3个地方改变你的代码,你应该罚款。

变化1

/*foreach(var eq in eqs) 
    { 
     eqList.Add(eq); 
    } 
    Equips = eqList; 
    RaisePropertyChanged("Equips");*/ 
    foreach(var eq in eqs) 
    { 
     Equips.Add(eq); 
    } 

变化2

/*foreach (var l in locs) 
    { 
     locList.Add(l); 
    } 
    Locations = locList; 
    RaisePropertyChanged("Locations");*/ 
    foreach (var l in locs) 
    { 
     Locations.Add(l); 
    } 

更改3

要么只是删除CollectionViewSource(这是什么为您提供?)的使用或使用绑定设置资源。由于您目前正在手动设置Source(即locationViewSource.Source = viewModel.Locations;),因此在引发PropertyChanged事件时,您已选择不更新该值。

所以,如果你只是删除CollectionViewSource,那么你只需要绑定到Locations属性。如果你决定把CollectionViewSource那么我会建议删除页面codebhind,只是改变了XAML来

<CollectionViewSource x:Key="locationsVwSrc" Source="{Binding Locations}" /> 
+0

感谢您的回复。你能否详细说明如何绑定到locations属性。我已经尝试了很多方法来做到这一点,我似乎永远无法得到它的正确。 CollectionViewSource是一个解决这个问题的方法。 – Asvaldr

+0

噢,对,我假设你的行上的datacontext是'Equip'类型,而不是父'EquipRegVm'。如果您想要取消回到父类型,您将需要使用RelativeSource。因此,如后面的代码所示,DataGrid绑定到“装备”,“绑定路径= DataContext.Locations,RelativeSource = {RelativeSource AncestorType = {x:Type DataGrid}}}”/> –

+0

“,而不是虚拟机。 进行建议的更改将我返回到错误 System.Windows.Data错误:4:找不到与参考绑定的源'RelativeSource FindAncestor,AncestorType ='System.Windows.Controls.DataGrid',AncestorLevel ='1 ''。 BindingExpression:路径=的DataContext。位置;的DataItem = NULL;目标元素是'DataGridComboBoxColumn'(HashCode = 57273980);目标属性是'ItemsSource'(类型'IEnumerable') – Asvaldr

相关问题