2014-09-09 9 views
5

Przeczytałem wszystkie powiązane artykuły tutaj na tablicy, ale nadal nie mogę rozwiązać mojego problemu, który mam podczas wiązania ObservableCollection do ListView.Zmiany w ObservableCollection nie aktualizują ListView

Mam klasę modelu CLogEntry, która zasadniczo opakowuje ciąg.

/// Model of LogEntry 
public class CLogEntry:INotifyPropertyChanged 
{ 
    /// Fields 
    private string _logEntry; 

    /// Property 
    public string LogEntry 
    { 
     get { return _logEntry; } 

     set 
     { 
      _logEntry = value; 
      RaisePropertyChanged("LogEntry"); 
     } 
    } 

    /// PropertyChanged event handler 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// Constructor 
    public CLogEntry(string logEntry) 
    { 
     this.LogEntry = logEntry; 
    } 

    /// Property changed Notification   
    public void RaisePropertyChanged(string propertyName) 
    { 
     // take a copy to prevent thread issues 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

W moim ViewModel Mam ObservableCollection, która posiada swoje obiekty CLogEntry jak również odpowiednią własność publiczną dla niego.

class CLoggerViewModel : INotifyPropertyChanged 
{ 
    /// Memory Appender object 
    private CMemoryAppender _memoryAppender; 
    /// ObservableCollection for LogEntries 
    private ObservableCollection<CLogEntry> _logEntries; 

    /// Property to expose ObservableCollection for UI 
    public ObservableCollection<CLogEntry> LogEntries 
    { 
     get { return _logEntries; } 
    } 

    /// Event for PropertyChanged Notification 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// Constructor of viewModel 
    public CLoggerViewModel() 
    { 
     this._logEntries = new ObservableCollection<CLogEntry>(); 
     this._memoryAppender = new CMemoryAppender(); 
     this._memoryAppender.PropertyChanged += new PropertyChangedEventHandler(OnMemoryAppenderPropertyChanged); 
     this._memoryAppender.LogContentChanged += new LoggingEventHandler(OnLogContentChanged); 
    } 

    /// Update collection 
    public void OnLogContentChanged(object sender, LoggingEventArgs e) 
    { 
     /// Here i add LogEntries event based to my collection. 
     /// For simplicity i just used a temporarly string here. 
     string[] tmpString = { "A", "B", "C", "D" }; 

     foreach (string s in tmpString) 
     { 
      this.LogEntries.Add(new CLogEntry(s)); 
     } 
    } 

    /// Any of the properties of the MemoryAppender objects has changed 
    private void OnMemoryAppenderPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     this.RaisePropertyChanged(e.PropertyName); 
    } 

    /// PropertyChanged EventHandler 
    public void RaisePropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

Mój kod XAML dla ListView jest następujący:

<ListView x:Name="lstLogs" DataContext ="{Binding LoggerViewModel}" ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0"> 
    <ListView.View> 
     <GridView x:Name="grdLogs"> 
      <GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntries}"/> 
     </GridView> 
    </ListView.View> 
</ListView> 

Moim problemem jest to, że lista nie pokazuje żadnych danych. Ale kiedy debuguję kod, widzę, że moja właściwość dla ObservableCollection zostaje wywołana i że moja kolekcja zawiera wszystkie dodane przeze mnie LogEntries. Zakładam więc, że zdarzenie CollectionChanged zostanie wyrzucone, a interfejs wywoła moją właściwość LogEntries. Ale nie rozumiem, dlaczego ListView nie wyświetla żadnych danych.

Czy występuje problem z moim kodem XAML lub czy jest to problem w modelu i/lub ViewModel?

EDIT:

Wreszcie problemem była kwestia gwintowania. Ponieważ klasa ObervableCollection jest tworzona przez wątek interfejsu użytkownika, generuje wyjątek, jeśli inny wątek dodaje/manipuluje kolekcją. Aby pozbyć się tego problemu, znalazłem następujące rozwiązanie, które implementuje Asynchronous ObservableCollection.

poniższych linków pomógł mi dostać pracy: Stackoverflow Implementing Async ObservableCollection

+0

Jak ustawić 'DataContext'? A kiedy tworzysz instancję 'LogEntries'? – har07

+0

pls użyć Snoop, aby sprawdzić DataContext i Binding Expression w czasie wykonywania – blindmeis

+0

@ blindemeis: Fałszywy mi mówi: DataContext - [Logger.CLoggerViewModel] {Path = LoggerViewModel}. Ale dla BindingExpression nie jestem pewien. Właśnie znalazłem Właściwości dla Binding.XmlNamespaceManager i BindingGroup dla ListView w Spoof. Ale obie właściwości nie trzymają żadnej wartości podczas wykonywania. – ck84vi

Odpowiedz

1

jeśli DataContext jest Twój ViewModel (CLoggerViewModel) następnie wiązanie ItemsSource powinno być:

<ListView ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0"> 

i wyrażenie wiążące do LogEntry powinien po prostu być {Binding LogEntry}

<GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntry}"/> 

EDIT:

  • zapomnieć IntelliSense w XAML!
  • Twój ListView ItemsSource musiał wiązać się z LogEntries obiektu w ViewModel CLoggerViewModel
  • GridViewColumn DisplayMemberBinding muszą wiązać się z logentry obiektu w swojej klasie CLogEntry

EDIT: do najnowszych aktualizacji

DataContext = "{Binding LoggerViewModel}" -> Co to jest? oznacza to, że potrzebna jest właściwość publiczna o nazwie LoggerViewModel na bieżącym Datacontext. Nie sądzę, że to, czego chcesz. Twój kod Viewmodel wygląda dobrze, ale problemem jest twój XAML i ustawienie twojego Datacontext. więc pls opublikuj kod, w którym ustawisz DataContext.

EDIT: Kod działa

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<ListView ItemsSource="{Binding LogEntries}"> 
    <ListView.View> 
     <GridView > 
      <GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntry}"/> 
     </GridView> 
    </ListView.View> 
</ListView> 
</Window> 

cs

public partial class MainWindow : Window 
{ 
    private CLoggerViewModel _vm = new CLoggerViewModel(); 
    public MainWindow() 
    { 
     InitializeComponent(); 
     this.DataContext = _vm; 
    } 
} 

public class CLogEntry : INotifyPropertyChanged 
{ 
    /// Fields 
    private string _logEntry; 

    /// Property 
    public string LogEntry 
    { 
     get { return _logEntry; } 

     set 
     { 
      _logEntry = value; 
      RaisePropertyChanged("LogEntry"); 
     } 
    } 

    /// PropertyChanged event handler 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// Constructor 
    public CLogEntry(string logEntry) 
    { 
     this.LogEntry = logEntry; 
    } 

    /// Property changed Notification   
    public void RaisePropertyChanged(string propertyName) 
    { 
     // take a copy to prevent thread issues 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

class CLoggerViewModel : INotifyPropertyChanged 
{ 
    /// Memory Appender object 
    //private CMemoryAppender _memoryAppender; 
    /// ObservableCollection for LogEntries 
    private ObservableCollection<CLogEntry> _logEntries; 

    /// Property to expose ObservableCollection for UI 
    public ObservableCollection<CLogEntry> LogEntries 
    { 
     get { return _logEntries; } 
    } 

    /// Event for PropertyChanged Notification 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// Constructor of viewModel 
    public CLoggerViewModel() 
    { 
     this._logEntries = new ObservableCollection<CLogEntry>(); 
     //dunno what CMemoryAppender is 
     //this._memoryAppender = new CMemoryAppender(); 
     //this._memoryAppender.PropertyChanged += new PropertyChangedEventHandler(OnMemoryAppenderPropertyChanged); 
     //this._memoryAppender.LogContentChanged += new LoggingEventHandler(OnLogContentChanged); 

     //thats why i fill my collection here 
     string[] tmpString = { "A", "B", "C", "D" }; 

     foreach (string s in tmpString) 
     { 
      this.LogEntries.Add(new CLogEntry(s)); 
     } 
    } 
    /// Any of the properties of the MemoryAppender objects has changed 
    private void OnMemoryAppenderPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     this.RaisePropertyChanged(e.PropertyName); 
    } 

    /// PropertyChanged EventHandler 
    public void RaisePropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 
+0

@ Blindemeis: Zmieniłem XAML zgodnie z twoją podpowiedź, jak pokazano w moim edytowanym poście. Ale teraz mój problem polega na tym, że moja iteracja pętli foreach, w której dodaję obiekty do kolekcji, zostanie zatrzymana po dodaniu pierwszego obiektu do listy. Miałem ten problem wcześniej i myślałem, że został rozwiązany przez zawiązanie łańcucha w klasie. Umieściłem to w tym wątku, ponieważ nie zdawałem sobie sprawy, że problem jest spowodowany przez część WPF. http://stackoverflow.com/questions/25722641/add-string-array-to-observablecollectionstring-does-notwork-work – ck84vi

+0

Powiązanie ItemsSource powinno być LogEntries, a nie LoggerViewModel. – blindmeis

+0

@ Blindemeis: To jest to, co mam -> ItemsSource = "{Binding LogEntries}". Zaktualizowałem to wcześniej w moim poście. Tak jak powiedziałem. Odkąd to zrobiłem, moja pętla foreach zatrzymuje dodawanie obiektów do kolekcji po dodaniu pierwszego obiektu. – ck84vi

Powiązane problemy