2014-09-09 11 views
5

Ich habe alle verwandten Artikel hier in der Tafel gelesen, aber ich kann immer noch nicht mein Problem lösen, das ich beim Binden einer ObservableCollection an eine ListView habe.Änderungen in ObservableCollection aktualisieren nicht ListView

Ich habe eine CLogEntry-Modellklasse, die im Grunde eine Zeichenfolge umschließt.

/// 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 meinem Viewmodel habe ich eine ObservableCollection, die für sie meine CLogEntry Objekte sowie die entsprechenden öffentlichen Eigentum hält.

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

Mein XAML-Code für das Listview ist die folgende:

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

Mein Problem ist, dass die Liste keine Daten zeigen. Aber wenn ich den Code debuggen sehe ich, dass meine Eigenschaft für die ObservableCollection aufgerufen wird und dass meine Sammlung alle LogEntries enthält, die ich hinzufüge. Also nehme ich an, dass das CollectionChanged-Ereignis ausgelöst wird und die Benutzeroberfläche meine LogEntries-Eigenschaft aufruft. Aber ich verstehe nicht, warum das ListView keine Daten zeigt.

Gibt es ein Problem mit meinem XAML-Code oder ist es ein Problem in Model und/oder ViewModel?

EDIT:

Schließlich war das Problem ein Threading-Problem. Da die ObervableCollection vom UI-Thread erstellt wird, löst sie eine Ausnahme aus, wenn ein anderer Thread die Sammlung hinzufügt/bearbeitet. Um dieses Problem zu beheben, habe ich die folgende Lösung gefunden, die eine asynchrone ObservableCollection implementiert.

ein Link hat mir geholfen, es zum Laufen zu bringen: Stackoverflow Implementing Async ObservableCollection

+0

Wie Sie die 'DataContext' eingestellt? Und wenn Sie die 'LogEntries'-Eigenschaft instanziieren? – har07

+0

Verwenden Sie Snoop, um Ihren DataContext und Binding Ausdruck zur Laufzeit zu überprüfen – blindmeis

+0

@blindemeis: Spoof sagt mir: DataContext - [Logger.CLoggerViewModel] {Path = LoggerViewModel}. Aber für die BindingExpression bin ich nicht sicher. Ich habe gerade Eigenschaften für Binding.XmlNamespaceManager und BindingGroup für die ListView in Spoof gefunden. Aber beide Eigenschaften enthalten keinen Wert zur Laufzeit. – ck84vi

Antwort

1

wenn der Datacontext ist Ihr Viewmodel (CLoggerViewModel) dann die Itemssource-Bindung sollte sein:

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

und der Bindungsausdruck zu Ihrem Logeintrag sollte einfach {Binding LogEntry}

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

EDIT:

  • vergessen IntelliSense in XAML!
  • Ihre Listview Itemssource zum Immobilien LogEntries in Ihrem Ansichtsmodell CLoggerViewModel
  • die Gridviewcolumn DisplayMemberBinding binden müssen Immobilien LogEntry in Ihrer Klasse CLogEntry

EDIT binden: um Ihre neuesten Updates

DataContext = "{Binding LoggerViewModel}" -> Was ist das? Das bedeutet, dass Sie eine öffentliche Eigenschaft namens LoggerViewModel für Ihren aktuellen Datacontext benötigen. Ich denke nicht das ist was du willst. Ihr Viewmodel-Code sieht gut aus, aber das Problem ist Ihr XAML und das Setzen Ihres Datacontext. So geben Sie den Code ein, in dem Sie den DataContext festgelegt haben.

EDIT: Arbeits 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)); 
     } 
    } 
} 
+0

@blindemeis: Ich habe den XAML nach deinem Hinweis geändert, wie in meinem bearbeiteten Post angezeigt. Aber jetzt ist mein Problem, dass meine Foreach-Schleife Iteration, wo ich die Objekte der Sammlung hinzufügen wird nach dem Hinzufügen des ersten Objekts zur Liste gestoppt. Ich hatte dieses Problem vorher und dachte, dass es gelöst wurde, indem man die Schnur in einer Klasse wickelte. Ich habe dies bereits in diesem Thread gepostet, weil ich nicht erkannt habe, dass das Problem durch den WPF-Teil verursacht wird. http://stackoverflow.com/questions/25722641/add-string-array-to-observablecollectionstring-does-not-work – ck84vi

+0

die ItemsSource-Bindung sollte LogEntries und nicht LoggerViewModel sein. – blindmeis

+0

@blindemeis: Das habe ich -> ItemsSource = "{Binding LogEntries}". Ich habe das schon mal in meinem Post aktualisiert. Wie ich sagte. Seit ich dies getan habe, hört meine foreach-Schleife auf, Objekte zur Sammlung hinzuzufügen, nachdem das erste Objekt hinzugefügt wurde. – ck84vi

Verwandte Themen