Changes to ObservableCollection do not update ListView

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

+6
source share
1 answer

if the DataContext is your view model (CLoggerViewModel), then the Itemssource binding should be:

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

and the binding expression for your LogEntry should just be {Binding LogEntry}

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

EDIT:

  • forget IntelliSense in XAML!
  • ListView ItemsSource elements must bind to Property LogEntries properties in your ViewModel CLoggerViewModel
  • GridViewColumn DisplayMemberBinding must be associated with the Property LogEntry in the CLogEntry class

EDIT: to your latest updates

DataContext = "{Binding LoggerViewModel}" β†’ What is this? this means that you need the public property LoggerViewModel in the current Datacontext. I do not think you want. your Viewmodel code looks fine, but the problem is your XAML and setting up your Datacontext. therefore PLS sends the code to which you set the DataContext.

EDIT: working code

 <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)); } } } 
+1
source

Source: https://habr.com/ru/post/975010/


All Articles