2009-05-26 3 views
0

J'ai un listbox défini en XAML comme:WPF Listbox ne redessinant

<ListBox x:Name="directoryList" 
       MinHeight="100" 
       Grid.Row="0" 
       ItemsSource="{Binding Path=SelectedDirectories}"/> 

Les SelectedDirectories est une propriété sur les listes DataContext de type List<DirectoryInfo>

La classe qui est le DataContext pour la zone de liste met en œuvre INotifyPropertyChanged . Lorsque la collection change, les éléments sont ajoutés avec succès à la liste, mais l'affichage ne se met pas à jour tant que je ne force pas la liste à redessiner en la redimensionnant.

Des idées pourquoi?

EDIT: mise en œuvre 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)); 
      } 
     } 
    } 

Répondre

3

Essayez

ObservableCollection<DirectoryInfo> 

au lieu - vous déclencher une actualisation de l'ensemble ListBox sans raison, et vous n'avez pas besoin de faire de votre classe d'hébergement implémenter INotifyPropertyChanged - il pourrait facilement être juste une propriété de la fenêtre. La clé est de ne jamais définir la propriété à une nouvelle instance. Alors:

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

Si vous finissez par utiliser cette classe FileScanner, vous devez mettre en œuvre INotifyCollectionChanged à la place - de cette façon, le ListBox sait ce qu'il faut ajouter/supprimer dynamiquement.

+0

Vous avez raison w.r.t. pourquoi mon extrait de code précédent a fonctionné. Actualisé. +1 – Gishu

+0

Ce que j'ai fini par faire, c'est de faire abstraction de la collection dans une classe de présentateur dans une ObservableCollection et de la passer ensuite à la classe de scanner en cas de besoin. – benPearce

0

(Voir la mise à jour ci-dessous). WPF semble fonctionner correctement. Je mets ton code dans un nouveau projet. La zone de liste est mise à jour chaque fois que je clique sur le bouton pour appeler AddDirectory. Vous ne devriez plus avoir besoin de changements de code. Le problème semble être autre chose .. Y at-il plusieurs threads dans votre interface utilisateur?

Je n'avais pas le type FileScanner. J'ai donc créé un mannequin comme suit.

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

Aucune modification de votre classe FileScannerPresenter. Ou votre liste XAML. J'ai créé une fenêtre avec un DockPanel contenant votre listbox, une zone de texte et un bouton.

Mise à jour: Paul Betts a raison. Cela fonctionne parce que je renvoie une nouvelle liste à chaque fois de la propriété Bound. La liaison de données avec des listes me gâche toujours. Avec plus bricoler, la façon facile de le faire est la suivante:

  • Make FileScanner # répertoires renvoient un ObservableCollection<DirectoryInfo> (qui met en œuvre INotifyCollectionChanged pour vous). Changez toutes les signatures pour retourner ce type au lieu d'un List<DirectoryInfo>
  • FileScanner et FileScannerPresenter n'ont pas besoin d'implémenter une interface INotifyXXX.

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

Votre code fonctionne parce que le Getter de la liste <> renvoie une nouvelle liste à chaque fois - s'il renvoyait la même liste (c'est-à-dire une liste instanciée au fichier .ctor de FileScanner, cela ne fonctionnerait pas) –