ObservableCollection中的更改不会更新ListView

ck8*_*4vi 7 data-binding wpf listview observablecollection

我一直在阅读董事会中的所有相关文章但我仍然无法解决我将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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我对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>
Run Code Online (Sandbox Code Playgroud)

我的问题是该列表不显示任何数据.但是当我调试代码时,我可以看到我的ObservableCollection属性被调用,我的集合包含我添加的所有LogEntries.所以我假设CollectionChanged事件被触发,UI正在调用我的LogEntries属性.但我不明白为什么ListView不显示任何数据.

我的XAML代码是否有问题,或者它是模型和/或ViewModel中的问题?

编辑:

最后问题是线程问题.由于ObervableCollection是由UI线程创建的,因此如果另一个线程正在添加/操作集合,则会抛出异常.为了摆脱这个问题,我找到了以下实现Asynchronous ObservableCollection的解决方案.

以下链接帮助我实现了它: Stackoverflow 实现Async ObservableCollection

bli*_*eis 2

如果DataContext是您的视图模型 (CLoggerViewModel),那么 Itemssource 绑定应该是:

  <ListView ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0">
Run Code Online (Sandbox Code Playgroud)

LogEntry 的绑定表达式应该是 {Binding LogEntry}

  <GridViewColumn Header="Log Entry"  DisplayMemberBinding="{Binding Path=LogEntry}"/>
Run Code Online (Sandbox Code Playgroud)

编辑:

  • 忘记 XAML 中的 IntelliSense!
  • 您的 ListView ItemsSource 必须绑定到 Viewmodel CLoggerViewModel 中的 Property LogEntries
  • GridViewColumn DisplayMemberBinding 必须绑定到类 CLogEntry 中的 Property 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>
Run Code Online (Sandbox Code Playgroud)

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)