2014-09-09 23 views
5

Tôi đã đọc tất cả các bài viết liên quan ở đây trong bảng nhưng tôi vẫn không thể giải quyết được vấn đề của mình mà tôi có khi liên kết ObservableCollection với ListView.Thay đổi trong ObservableCollection không cập nhật ListView

Tôi có lớp mô hình CLogEntry về cơ bản bao bọc một chuỗi.

/// 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)); 
     } 
    } 
} 

Trong ViewModel, tôi có một ObservableCollection chứa các đối tượng CLogEntry của tôi cũng như tài sản công cộng tương ứng cho nó.

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)); 
     } 
    } 
} 

mã XAML của tôi cho ListView như sau:

<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> 

Vấn đề của tôi là danh sách không hiển thị bất kỳ dữ liệu. Nhưng khi tôi gỡ lỗi mã tôi có thể thấy rằng thuộc tính của tôi cho ObservableCollection được gọi và bộ sưu tập của tôi chứa tất cả các LogEntries mà tôi thêm vào. Vì vậy, tôi cho rằng sự kiện CollectionChanged được kích hoạt và giao diện người dùng đang gọi thuộc tính LogEntries của tôi. Nhưng tôi không hiểu tại sao ListView không hiển thị bất kỳ dữ liệu nào.

Có vấn đề với mã XAML của tôi hay có vấn đề trong mô hình và/hoặc ViewModel không?

EDIT:

Cuối cùng vấn đề là một vấn đề luồng. Vì ObervableCollection được tạo bởi chuỗi giao diện người dùng, nó sẽ đưa ra một ngoại lệ nếu một luồng khác đang thêm/thao tác bộ sưu tập. Để loại bỏ vấn đề này, tôi tìm thấy giải pháp sau đây thực hiện một ObservableCollection không đồng bộ.

liên kết sau giúp tôi để có được nó làm việc: Stackoverflow Implementing Async ObservableCollection

+0

Làm thế nào bạn thiết lập 'DataContext'? Và khi bạn khởi tạo thuộc tính 'LogEntries'? – har07

+0

hãy sử dụng Snoop để kiểm tra DataContext và Binding Expression của bạn tại thời điểm chạy – blindmeis

+0

@blindemeis: Spoof đang cho tôi biết: DataContext - [Logger.CLoggerViewModel] {Path = LoggerViewModel}. Nhưng đối với BindingExpression im không chắc chắn. tôi chỉ tìm thấy Properties cho Binding.XmlNamespaceManager và BindingGroup cho ListView trong Spoof. Nhưng cả hai thuộc tính không giữ bất kỳ giá trị trong thời gian chạy. – ck84vi

Trả lời

1

nếu DataContext là viewmodel của bạn (CLoggerViewModel) sau đó các ràng buộc ItemsSource nên là:

<ListView ItemsSource="{Binding LogEntries}" Margin="5,5,5,5" Grid.Column="1" Grid.Row="0"> 

và biểu thức ràng buộc đối với LogEntry của bạn chỉ đơn giản là {Binding LogEntry}

<GridViewColumn Header="Log Entry" DisplayMemberBinding="{Binding Path=LogEntry}"/> 

EDIT:

  • quên IntelliSense in XAML!
  • ListView bạn ItemsSource phải liên kết với LogEntries về tài sản trong ViewModel CLoggerViewModel bạn
  • các GridViewColumn DisplayMemberBinding phải liên kết với LogEntry về tài sản trong CLogEntry lớp học của bạn

EDIT: để cập nhật mới nhất của bạn

DataContext = "{Binding LoggerViewModel}" -> Cái gì vậy? điều này có nghĩa là bạn cần một tài sản công cộng được gọi là LoggerViewModel trên Datacontext hiện tại của bạn. tôi không nghĩ đó là những gì bạn muốn. mã ViewModel của bạn trông ok, nhưng vấn đề là XAML của bạn và thiết lập Datacontext của bạn. vì vậy xin đăng mã nơi bạn đặt DataContext.

EDIT: làm việc đang

<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)); 
     } 
    } 
} 
+0

@blindemeis: Tôi đã thay đổi XAML theo gợi ý của bạn như được hiển thị trong bài đăng đã chỉnh sửa của tôi. Nhưng bây giờ vấn đề của tôi là lặp vòng lặp foreach của tôi, nơi tôi thêm các đối tượng vào bộ sưu tập được dừng lại sau khi thêm đối tượng đầu tiên vào danh sách. Tôi đã có vấn đề này trước và nghĩ rằng nó đã được giải quyết bằng cách gói chuỗi trong một lớp học. Tôi đăng này trong chủ đề này đã được vì tôi didnt nhận ra rằng vấn đề là do phần WPF. http://stackoverflow.com/questions/25722641/add-string-array-to-observablecollectionstring-does-not-work – ck84vi

+0

các ItemSource Binding nên LogEntries và không LoggerViewModel. – blindmeis

+0

@blindemeis: Đó là những gì tôi có -> ItemsSource = "{Binding LogEntries}". Tôi đã cập nhật thông tin này trước khi đăng bài. Như tôi đa noi. Vì tôi đã làm điều này vòng lặp foreach của tôi dừng lại thêm các đối tượng vào bộ sưu tập sau khi thêm đối tượng đầu tiên. – ck84vi

Các vấn đề liên quan