I read all related articles here on the board, but I still canβt solve my problem with binding an ObservableCollection to a ListView.
I have a CLogEntry model class that basically wraps a string.
/// 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)); } } }
In my ViewModel, I have an ObservableCollection that contains my CLogEntry objects, as well as its corresponding public property.
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)); } } }
My XAML code for ListView is as follows:
<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>
My problem is that there is no data in the list. But when I debug the code, I see that my property for the ObservableCollection is called and that my collection contains all the LogEntries that I add. Therefore, I assume that the CollectionChanged event is fired, and the user interface calls the LogEntries property. But I do not understand why the ListView does not show any data.
Is there a problem with my XAML code or is it a problem in the model and / or ViewModel?
EDIT:
Finally, the problem was the problem with threads. Since the ObervableCollection is created by the user interface thread, it throws an exception if another thread adds / controls the assembly. To get rid of this problem, I found the following solution that implements an asynchronous ObservableCollection.
The following links helped me get it to work: https://stackoverflow.com/a/412930/2128/ Implementation of Async ObservableCollection