wpf ObservableCollection 的变化不会更新 ListView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25736802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Changes in ObservableCollection do not update ListView
提问by ck84vi
I have been reading all the related articles here in the board but I still can't solve my problem that I have when binding an ObservableCollection to a ListView.
我一直在阅读板上的所有相关文章,但仍然无法解决将 ObservableCollection 绑定到 ListView 时遇到的问题。
I have a CLogEntry model class which basically wraps a string.
我有一个 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));
}
}
}
In my ViewModel I have an ObservableCollection that holds my CLogEntry objects as well as the corresponding public property for it.
在我的 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));
}
}
}
My XAML code for the ListView is the following:
我的 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>
My Problem is that the list does not show any data. But when I debug the code I can see that my property for the ObservableCollection gets called and that my collection holds all the LogEntries that I add. So I assume that the CollectionChanged event gets fired and the UI is calling my LogEntries property. But I don't understand why the ListView does not show any data.
我的问题是该列表没有显示任何数据。但是当我调试代码时,我可以看到我的 ObservableCollection 属性被调用,并且我的集合保存了我添加的所有 LogEntries。所以我假设 CollectionChanged 事件被触发并且 UI 正在调用我的 LogEntries 属性。但我不明白为什么 ListView 不显示任何数据。
Is there a problem with my XAML code or is it a problem in model and/or ViewModel?
我的 XAML 代码有问题还是模型和/或 ViewModel 有问题?
EDIT:
编辑:
Finally the problem was a threading issue. Since the ObervableCollection is created by the UI thread it throws an exception if another thread is adding/manipulating the collection. To get rid of this problem i found the following solution that implements an Asynchronous ObservableCollection.
最后,问题是线程问题。由于 ObervableCollection 是由 UI 线程创建的,如果另一个线程正在添加/操作集合,它会引发异常。为了摆脱这个问题,我找到了以下实现异步 ObservableCollection 的解决方案。
Following links helped me to get it working: StackoverflowImplementing Async ObservableCollection
以下链接帮助我让它工作: Stackoverflow实现异步 ObservableCollection
采纳答案by blindmeis
if the DataContextis your viewmodel (CLoggerViewModel) then the Itemssource binding should be:
如果DataContext是您的视图模型 (CLoggerViewModel),那么 Itemssource 绑定应该是:
<ListView ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0">
and the binding expression to your LogEntry should simply be {Binding LogEntry}
并且您的 LogEntry 的绑定表达式应该只是 {Binding LogEntry}
<GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntry}"/>
EDIT:
编辑:
- forget IntelliSense in XAML!
- your ListView ItemsSource have to bind to the Property LogEntries in your Viewmodel CLoggerViewModel
- the GridViewColumn DisplayMemberBinding have to bind to the Property LogEntry in your class CLogEntry
- 忘记 XAML 中的智能感知!
- 您的 ListView ItemsSource 必须绑定到您的 Viewmodel CLoggerViewModel 中的属性 LogEntries
- GridViewColumn DisplayMemberBinding 必须绑定到类 CLogEntry 中的属性 LogEntry
EDIT: to your latest updates
编辑:到您的最新更新
DataContext ="{Binding LoggerViewModel}" --> What is that? this means you need a public property called LoggerViewModel on your current Datacontext. i dont think thats what you want. your Viewmodel code looks ok, but the problem is your XAML and setting your Datacontext. so pls post the code where you set the DataContext.
DataContext ="{Binding LoggerViewModel}" --> 那是什么?这意味着您需要在当前 Datacontext 上有一个名为 LoggerViewModel 的公共属性。我不认为那是你想要的。您的 Viewmodel 代码看起来不错,但问题在于您的 XAML 和设置您的 Datacontext。所以请发布您设置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
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));
}
}
}

