ObservableCollection中的更改不会更新ListView

7
我已经阅读了论坛中所有相关文章,但在将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线程创建的,如果另一个线程正在添加/操作集合,则会抛出异常。 为了解决这个问题,我找到了以下解决方案,实现了一个异步ObservableCollection。
以下链接帮助我让它工作: Stackoverflow Implementing Async ObservableCollection

你如何设置DataContext?并且在何时实例化LogEntries属性? - har07
请使用Snoop在运行时检查您的DataContext和Binding Expression。 - blindmeis
@blindemeis: Spoof 告诉我:DataContext - [Logger.CLoggerViewModel] {Path = LoggerViewModel}。但是对于 BindingExpression,我不确定。我在 Spoof 中为 Binding.XmlNamespaceManager 和 BindingGroup 找到了属性,但是这两个属性在运行时都没有任何值。 - ck84vi
1个回答

2

如果DataContext是您的视图模型(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}"/>

编辑:

  • 在XAML中忘记IntelliSense!
  • 您的ListView ItemsSource必须绑定到您的Viewmodel CLoggerViewModel中的属性LogEntries。
  • GridViewColumn DisplayMemberBinding必须绑定到您的类CLogEntry中的属性LogEntry。

编辑:针对您的最新更新

DataContext ="{Binding LoggerViewModel}"-->这是什么?这意味着您需要在当前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));
        }
    }
}

@blindemeis:根据您的提示,我已经更改了XAML,如我编辑的帖子所示。但现在我的问题是,在将对象添加到集合的foreach循环迭代中,添加第一个对象后就停止了。我以前遇到过这个问题,并认为通过将字符串包装在类中解决了它。我已经在此线程中发布了此内容,因为我没有意识到问题是由WPF部分引起的。http://stackoverflow.com/questions/25722641/add-string-array-to-observablecollectionstring-does-not-work - ck84vi
@blindemeis:这就是我拥有的-> ItemsSource="{Binding LogEntries}"。我之前在帖子中更新过这个。正如我所说,自从我这样做后,我的foreach循环在添加第一个对象后停止向集合中添加对象。 - ck84vi
你的构造函数里有这个吗:_logEntries = new ObservableCollection<CLogEntry>();?我的测试项目可以使用你的代码。顺便说一下,我在DisplayMemberBinding上有一个小错误。请看我的更新。 - blindmeis
@blindemeis:是的,我在构造函数中实例化它。我会相应地更新我的帖子。但如果我使用你的XAML,它就不起作用。我发现如果我设置 DisplayMemberBinding="{Binding Path=LogEntry}" ,IntelliSense 就不会为我提供 LogEntry 属性。我只能获取由我的 VM 提供的 LogEntries。所以我只能绑定整个集合而不是 LogEntry 属性本身。这可能是因为我在 ViewModel 或 Model 中有任何问题吗? - ck84vi
问题在于,如果我在构造函数之外添加对象到集合中,就会出现异常-> ItemsControl与其项源不一致!因此,我认为主要问题是线程问题。对于任何解决此问题的提示,我非常感激! - ck84vi
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接