我一直在阅读董事会中的所有相关文章,但我仍然无法解决将ObservableCollection绑定到ListView时遇到的问题。
我有一个CLogEntry模型类,它基本上包装了一个字符串。
/// 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));
}
}
}
在我的ViewModel中,我有一个ObservableCollection,它包含我的CLogEntry对象以及它的相应公共属性。
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));
}
}
}
我对ListView的XAML代码如下:
<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>
我的问题是列表没有显示任何数据。 但是当我调试代码时,我可以看到我的ObservableCollection属性被调用,我的集合包含我添加的所有LogEntries。 所以我假设CollectionChanged事件被触发,UI正在调用我的LogEntries属性。 但我不明白为什么ListView不会显示任何数据。
我的XAML代码是否有问题,或者它是模型和/或ViewModel中的问题?
修改
最后问题是线程问题。 由于ObervableCollection是由UI线程创建的,因此如果另一个线程正在添加/操作集合,则会抛出异常。 为了解决这个问题,我找到了以下实现Asynchronous ObservableCollection的解决方案。
以下链接帮助我实现了目标: Stackoverflow Implementing Async ObservableCollection
答案 0 :(得分:2)
如果 DataContext 是您的viewmodel(CLoggerViewModel),那么Itemssource绑定应为:
<ListView ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0">
和LogEntry的绑定表达式应该只是{Binding LogEntry}
<GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntry}"/>
编辑:
编辑:到您的最新更新
DataContext =&#34; {Binding LoggerViewModel}&#34; - &GT;那是什么?这意味着您需要在当前的Datacontext上使用名为LoggerViewModel的公共属性。我不认为那就是你想要的。您的Viewmodel代码看起来没问题,但问题是您的XAML并设置您的Datacontext。所以请在您设置DataContext的地方发布代码。
编辑:工作代码
<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));
}
}
}