WPF list is not redrawn
I have a list defined in XAML as:
<ListBox x:Name="directoryList"
MinHeight="100"
Grid.Row="0"
ItemsSource="{Binding Path=SelectedDirectories}"/>
SelectedDirectories is a property on DataContext lists of type List<DirectoryInfo>
The class that represents the datacontext for the listbox implements INotifyPropertyChanged. When the collection changes, the items are successfully added to the list, however, the display does not update until I force me to rename the list by resizing it.
Any ideas why?
EDIT: Implementation of INotifyPropertyChanged
public class FileScannerPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private FileScanner _FileScanner;
public FileScannerPresenter()
{
this._FileScanner = new FileScanner();
}
public List<DirectoryInfo> SelectedDirectories
{
get
{
return _FileScanner.Directories;
}
}
public void AddDirectory(string path)
{
this._FileScanner.AddDirectory(path);
OnPropertyChanged("SelectedDirectories");
}
public void OnPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
a source to share
Try
ObservableCollection<DirectoryInfo>
instead, you trigger an update of the entire ListBox for no reason, and you don't need your hosting class to implement INotifyPropertyChanged - it could easily be just a property of the window. The key is to never set the property to a new instance. So:
class SomeWindow : Window {
public ObservableCollection<DirectoryInfo> SelectedDirectories {get; private set;}
SomeWindow() { SelectedDirectories = new ObservableCollection<DirectoryInfo>(); }
public void AddDirectory(string path) {
SelectedDirectories.Add(new DirectoryInfo(path));
}
}
If you end up using this FileScanner class, you need to implement INotifyCollectionChanged - this way the ListBox knows what to add / remove dynamically.
a source to share
(see the Update section below) . WPF seems to be working fine. I have put your code into a new project. The list updates when I click the button to call AddDirectory. You don't need to change your code anymore. The problem seems to be something else. Are there multiple threads in the UI?
I didn't have a FileScanner type. So I created a dummy as follows.
public class FileScanner
{
string _path;
public FileScanner()
{ _path = @"c:\"; }
public List<DirectoryInfo> Directories
{
get
{
return Directory.GetDirectories(_path).Select(path => new DirectoryInfo(path)).ToList();
}
}
internal void AddDirectory(string path)
{ _path = path; }
}
No changes to your FileScannerPresenter class. Or your XAML list. I created a window with a DockPanel containing your list box, text box and button.
Update: Paul Betts is right. This works because every time I return a new list from the Bound property. Data binding with lists always pushes me. With great skill, an easy way to do it:
- Make FileScanner # of directories return
ObservableCollection<DirectoryInfo>
(which implementsINotifyCollectionChanged
for you). Change all signatures completely to return this type insteadList<DirectoryInfo>
-
FileScanner and FileScannerPresenter themselves do not need to implement any INotifyXXX interface.
// in FileScanner class def public ObservableCollection<DirectoryInfo> Directories { get { return _DirList; } } internal void AddDirectory(string path) { _path = path; //var newItems = Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList(); //_DirList.Concat( newItems ); -- doesn't work for some reason. foreach (var info in Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList()) { _DirList.Add(info); } }
a source to share