2016-10-08 59 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

相關問題