2016-10-08 17 views
1

데이터 그리드의 ComboBox에 막대한 문제가 있습니다. 그리고 정말로 도움이 필요 하겠지만, 나는 연구의 양과 내가 시도한 것에 혼란스러워했다. 이것은 정말 간단해야 그래서 뭔가를 놓치고 있어야합니다.CollectionViewSource가 PropertyChanged로 업데이트되지 않습니다.

약식 문제는 XAML에서 CollectionViewSource를 사용

는 C#을 페이지의 데이터 컨텍스트입니다 클래스에 ObservableCollection에 해당 CollectionViewSource의 소스를 설정합니다. 컬렉션에 항목을 추가해도 뷰 소스를 표시하는 DataGridComboBox 열이 업데이트되지 않습니다. 자세한 내용


개요

나는 그것에 데이터 그리드와 WPF 페이지를위한 라인 아래를 참조하십시오. 페이지의 데이터 컨텍스트가 뷰 모델로 설정되어 있습니다. viewModel에는 2 개의 관찰 가능한 컬렉션이 있습니다. 하나는 장비를위한 것이고 다른 하나는 위치를위한 것입니다. 각 장비에는 위치가 있습니다. 이들은 코드 첫 번째 EF 데이터베이스에서 채워지지만이 문제는 그 수준 이상이라고 생각합니다.

Dataagrid는 장비 당 하나의 행입니다. 위치 열은 사용자가 위치를 변경할 수있는 선택 가능한 콤보 박스 여야합니다.

내가 수집 할 위치 콤보 박스를 얻는 유일한 방법은 별도의 콜렉션 뷰 소스에 바인딩하는 것입니다.

문제

페이지로드 이벤트가 ObservableCollection에 채우기 전에 뷰 모델에 발생하면 다음 locationVwSrc가 비어과 재산 변경된 경우이 변경하지 않는 것 같다.

구현 짧은 버전 페이지에 컬렉션보기 소스가 xaml에 정의되어 있습니다.

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

Dataagrid는 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}" 

컨텍스트를

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(); 
    } 

뷰 모델 클래스를 설정하고,보기 모델

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

    public EquipRegPage() 
    { 
     InitializeComponent(); 
     this.DataContext = 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

에 XAML을 변경 제안 :

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. 리 캠벨 (Lee Campbell)은 내가 그에게서 더 많은 것을 배울 수있는 좋은 점을 분명히하고 있습니다. – Asvaldr

+0

예, 그러면 문제가 해결되지만 더 많은 불필요한 코드가 추가됩니다. –

+0

나는 당신이 더 이상 통찰력을 가지면 배우고 싶어한다는 Lee에게 동의합니다. 귀하의 솔루션에 대한 마지막 코멘트를 참조하십시오. – Asvaldr

2

문제를 충분히 해결할 수없는 것 같습니다.질문은 Datagrid의 ComboBoxes를 비동기식으로 CollectionViewSource 소스를 설정하고 데이터베이스에서 데이터를로드하는 것 같습니다. 나는 미리 준비된 데이터 부분, 즉 XAML 파일과 뷰 모델을 이동 최소 문제 (또는 soultion를) 다시

  1. 고려 중 하나에 도움이 될 것이라는 점을 시사한다.

  2. 또는 기존 코드를 분리합니다. Page가 ViewModel을 명시 적으로 알고있는 것 같습니다 (EquipRegVm viewModel = new EquipRegVm();). ViewModel은 데이터베이스에 대해 명시 적으로 알고 있으며 자체로드 방법을 알고 있습니다. 오, 이제 우리의 견해가 데이터베이스에 결합됩니까? MVVM과 같은 패턴이 우리가 결합되지 않았습니까?

다음은 일부 코드를보고 (패턴이라고 부르는) 안티 패턴을 좀 더 살펴 보겠습니다.

  • 설정 가능 콜렉션 특성 페이지에 대한 뒤에
  • 코드 (모두가 XAML에서 살 수)

하지만 당신은 단지 3 곳에서 코드를 변경 한 경우 당신은 괜찮을 기본적으로 생각합니다.

변경 한

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

변경이

/*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 삭제 단지 Binding 아래와 같이 설정

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

답장을 보내 주셔서 감사합니다. 위치 속성에 바인딩하는 방법에 대해 자세히 설명 할 수 있습니까? 나는 그것을하기위한 많은 방법을 시도해 보았고, 나는 결코 그것을 올바르게 보이지 않을 수 있습니다. CollectionViewSource는이를 해결하기위한 작업이었습니다. – Asvaldr

+0

아 맞아, 그 행의 datacontext는 부모의'EquipRegVm'이 아닌'Equip' 유형이라고 가정합니다. 상위 유형으로 연결을 해제하려면 RelativeSource를 사용해야합니다. ' –

+0

포스트 코드에서와 같이 DataGrid는 "Equips "VM이 아닙니다. 제안 된 변경 내용을 오류로 반환했습니다. 참조 : 'RelativeSource FindAncestor, AncestorType ='System.Windows.Controls.DataGrid ', AncestorLevel ='1 '참조로 바인딩 원본을 찾을 수 없습니다. ''. BindingExpression : Path = DataContext입니다.위치; DataItem = null; 대상 요소는 'DataGridComboBoxColumn'(HashCode = 57273980)입니다. 대상 속성이 'ItemsSource'(유형 'IEnumerable') – Asvaldr