2014-09-09 11 views
5

Ho letto tutti gli articoli correlati qui nella scheda ma non riesco ancora a risolvere il mio problema che ho quando leghiamo una ObservableCollection a un ListView.Le modifiche in ObservableCollection non aggiornano ListView

Ho una classe di modello CLogEntry che fondamentalmente avvolge una stringa.

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

Nel mio ViewModel Ho un ObservableCollection che contiene i miei oggetti CLogEntry nonché la corrispondente proprietà pubblica per esso.

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

Il mio codice XAML per il controllo ListView è la seguente:

<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> 

il mio problema è che l'elenco non mostra alcun dato. Ma quando eseguo il debug del codice, posso vedere che la mia proprietà per ObservableCollection viene chiamata e che la mia raccolta contiene tutti i LogEntries che aggiungo. Quindi presumo che l'evento CollectionChanged venga generato e che l'interfaccia utente stia chiamando la mia proprietà LogEntries. Ma non capisco perché ListView non mostri alcun dato.

C'è un problema con il mio codice XAML o è un problema nel modello e/o nel ViewModel?

EDIT:

Infine, il problema è stato un problema di threading. Poiché ObervableCollection viene creato dal thread dell'interfaccia utente, genera un'eccezione se un altro thread sta aggiungendo/manipolando la raccolta. Per risolvere questo problema, ho trovato la seguente soluzione che implementa una ObservableCollection asincrona.

seguenti link mi ha aiutato a farlo funzionare: Stackoverflow Implementing Async ObservableCollection

+0

Come si imposta il 'DataContext'? E quando istanziate la proprietà 'LogEntries'? – har07

+0

pls usa Snoop per controllare DataContext e Binding Expression in fase di esecuzione – blindmeis

+0

@blindemeis: Spoof mi sta dicendo: DataContext - [Logger.CLoggerViewModel] {Path = LoggerViewModel}. Ma per BindingExpression non sono sicuro. Ho appena trovato le proprietà per Binding.XmlNamespaceManager e BindingGroup per ListView in Spoof. Ma entrambe le proprietà non contengono alcun valore durante il runtime. – ck84vi

risposta

1

se il DataContext è il vostro ViewModel (CLoggerViewModel) allora il legame ItemsSource dovrebbe essere:

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

e l'espressione vincolante al tuo LogEntry dovrebbe essere semplicemente {Binding LogEntry}

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

EDIT:

  • dimentichiamo IntelliSense in XAML!
  • tuo ListView ItemsSource devono legarsi al LogEntries proprietà nel vostro ViewModel CLoggerViewModel
  • il GridViewColumn DisplayMemberBinding deve legarsi al LogEntry proprietà nella classe CLogEntry

EDIT: per gli ultimi aggiornamenti

DataContext = "{Binding LoggerViewModel}" -> Che cos'è? questo significa che hai bisogno di una proprietà pubblica chiamata LoggerViewModel sul tuo attuale Datacontext. non penso che sia quello che vuoi il tuo codice Viewmodel sembra ok, ma il problema è il tuo XAML e l'impostazione del tuo Datacontext. quindi, inserisci il codice in cui imposti DataContext.

EDIT: il codice di lavoro

<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: Ho modificato XAML in base al tuo suggerimento, come mostrato nel mio post modificato. Ma ora il mio problema è che la mia iterazione del ciclo foreach in cui aggiungo gli oggetti alla raccolta viene interrotta dopo aver aggiunto il primo oggetto alla lista. Ho avuto questo problema prima e ho pensato che fosse stato risolto avvolgendo la stringa in una classe. Ho postato questo in questo thread già perché non ho capito che il problema è causato dalla parte WPF. http://stackoverflow.com/questions/25722641/add-string-array-to-observablecollectionstring-does-not-work – ck84vi

+0

il binding di ItemsSource deve essere LogEntries e non LoggerViewModel. – blindmeis

+0

@blindemeis: questo è quello che ho -> ItemsSource = "{Binding LogEntries}". Ho aggiornato questo prima nel mio post. Come ho detto. Da quando l'ho fatto, il mio ciclo foreach interrompe l'aggiunta di oggetti alla raccolta dopo aver aggiunto il primo oggetto. – ck84vi

Problemi correlati