ObservableCollection中的更改不会更新ListView

时间:2014-09-09 04:38:44

标签: wpf listview data-binding 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));
        }
    }
}

在我的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

1 个答案:

答案 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}"/>

编辑:

  • 忘记了XAML中的智能感知!
  • 您的ListView ItemsSource必须绑定到Viewmodel CLoggerViewModel中的Property LogEntries
  • GridViewColumn DisplayMemberBinding必须绑定到类CLogEntry中的Property 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));
        }
    }
}