ListBox databinding not updating properly
I am getting a strange result where the data binding to a ListBox
works when the ObservableCollection
it is bound to is populated in the view model constructor but not when I move the same ObservableCollection
population code to an Button
event (tried from code behind event handler on the view and from a RoutedCommand
). There are no async
web services or background threads.
When I step through the code, the ObservableCollection
in the view model is still populated when called from the button event but the data is not updated on the ListBox
. The XAML is identical in both.
Here is some code:
XAML
<ListBox ItemsSource="{Binding Books}" BorderBrush="{x:Null}" Name="Results">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="12" FontWeight="Bold" />
<TextBlock Text="{Binding ID}" FontSize="10" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
...
<Button Command="{x:Static commands:BookQueryCommands.Query}" Content="Search" Width="119" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,25,0,0"/>
View code behind
public partial class BookExplorer : UserControl
{
private BookExplorerViewModel vm;
public BookExplorer()
{
this.InitializeComponent();
vm = new BookExplorerViewModel();
this.DataContext = vm;
this.CommandBindings.Add(new CommandBinding(BookQueryCommands.Query, ExecuteQuery));
}
public void ExecuteQuery(object sender, ExecutedRoutedEventArgs e) {
vm.ExecuteBookQuery();
}
}
ViewModel
public class BookExplorerViewModel : DependencyObject
{
private IBookListProvider bookProvider;
private bool canQuery = true;
public ObservableCollection<BookModel> Books { get; set; }
public string Title { get; set; }
public string ID { get; set; }
public DateTime LastModifiedDate { get开发者_运维百科; set; }
public BookExplorerViewModel() {
bookProvider = new BookListProvider();
}
public void ExecuteBookQuery() {
Books = bookProvider.DocumentsQuery("temp");
}
}
// bookProvider.DocumentsQuery("temp") just fills with dummy data.
// Binding works fine if the code in ExecuteBookQuery() is in the VM constructor
// Books is still populated fine in either case just binding is not updated
Model
public class BookModel : INotifyPropertyChanged
{
private string title;
public string Title {
get { return title; }
set {
if (value != this.title) {
this.title = value;
NotifyPropertyChanged(Title);
}
}
}
private string id;
public string ID {
get { return id; }
set {
if (value != this.id) {
this.id = value;
NotifyPropertyChanged(ID);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
The Books
property setter in view model doesn't fire PropertyChanged
and you're binding to it, no? When you assign an object to the Books
property, the View then binds to it. When you assign something else to the Books
, it's an entirely different object and the View has no idea about it unless you fire an event saying that the binding should be rewired to this new collection.
I changed the view model to:
public class BookExplorerViewModel : DependencyObject
{
private IBookListProvider bookProvider;
private bool canQuery = true;
public ObservableCollection<BookModel> Books { get; set; }
public string Title { get; set; }
public string ID { get; set; }
public DateTime LastModifiedDate { get; set; }
public BookExplorerViewModel() {
bookProvider = new BookListProvider();
Books = new ObservableCollection<BookModel>();
}
public void ExecuteBookQuery() {
Books.Clear();
foreach(BookModel book in bookProvider.DocumentsQuery("temp")){
Books.Add(book);
}
}
}
It works fine now.
精彩评论