Bindings für TreeView

  • WPF

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    Bindings für TreeView

    Hallo zusammen.

    Ich beschäftige mich ja in letzter Zeit ein wenig mit WPF. Einfach Bindings bekomme ich inzwischen recht gut hin und MVVM hab ich soweit denke ich auch verstanden.
    Jetzt wollte ich mich aber mal an ein paar komplexere Sachen ranwagen und hab mir deshalb ein kleines Testprojekt angelegt. Ziel soll es ein einen ganz einfachen Explorer-Verschnitt zu erstellen, also links ein Treeview mit den Ordnern und rechts dann ein ListView für die Dateien.
    Ich hab mir auch schon ein Model erstellt, welches so aussieht:
    Spoiler anzeigen

    C-Quellcode

    1. namespace SimpleExplorer
    2. {
    3. public abstract class FileSystemTreeViewItem : INotifyPropertyChanged
    4. {
    5. bool expanded;
    6. FileSystemTreeViewItem[] childItems;
    7. public bool Expanded
    8. {
    9. get
    10. {
    11. return expanded;
    12. }
    13. set
    14. {
    15. expanded = value;
    16. if (value) OnExpand();
    17. }
    18. }
    19. public abstract DirectoryInfo Directory { get; }
    20. public FileSystemTreeViewItem[] ChildItems
    21. {
    22. get
    23. {
    24. return childItems;
    25. }
    26. protected set
    27. {
    28. childItems = value;
    29. if (PropertyChanged != null)
    30. PropertyChanged(this, new PropertyChangedEventArgs("ChildItems"));
    31. }
    32. }
    33. protected abstract void OnExpand();
    34. public event PropertyChangedEventHandler PropertyChanged;
    35. }
    36. public class DriveTreeViewItem : FileSystemTreeViewItem
    37. {
    38. DriveInfo drive;
    39. DirectoryInfo directory;
    40. public static DriveTreeViewItem[] GetDrives()
    41. {
    42. return DriveInfo.GetDrives().Select(item => new DriveTreeViewItem(item)).ToArray();
    43. }
    44. private DriveTreeViewItem(DriveInfo drive)
    45. {
    46. this.drive = drive;
    47. directory = drive.RootDirectory;
    48. ChildItems = new[] { new EmptyTreeViewItem() };
    49. }
    50. public override DirectoryInfo Directory
    51. {
    52. get { return directory; }
    53. }
    54. protected override void OnExpand()
    55. {
    56. if (drive.IsReady)
    57. ChildItems = directory.EnumerateDirectories().Select(item => new DirectoryTreeViewItem(item)).ToArray();
    58. else
    59. ChildItems = new FileSystemTreeViewItem[0];
    60. }
    61. }
    62. public class DirectoryTreeViewItem : FileSystemTreeViewItem
    63. {
    64. DirectoryInfo directory;
    65. public DirectoryTreeViewItem(DirectoryInfo directory)
    66. {
    67. this.directory = directory;
    68. ChildItems = new[] { new EmptyTreeViewItem() };
    69. }
    70. public override DirectoryInfo Directory
    71. {
    72. get { return directory; }
    73. }
    74. protected override void OnExpand()
    75. {
    76. ChildItems = directory.EnumerateDirectories().Select(item => new DirectoryTreeViewItem(item)).ToArray();
    77. }
    78. }
    79. public class EmptyTreeViewItem : FileSystemTreeViewItem
    80. {
    81. public override DirectoryInfo Directory
    82. {
    83. get { return null; }
    84. }
    85. protected override void OnExpand() { }
    86. }
    87. }

    Und ein passendes ViewModel hab ich auch schon:
    Spoiler anzeigen

    C-Quellcode

    1. public class MainViewModel : INotifyPropertyChanged
    2. {
    3. static MainViewModel instance;
    4. FileSystemTreeViewItem[] treeViewItems;
    5. public static MainViewModel Instance
    6. {
    7. get
    8. {
    9. return instance ?? (instance = new MainViewModel());
    10. }
    11. }
    12. private MainViewModel()
    13. {
    14. TreeViewItems = DriveTreeViewItem.GetDrives();
    15. }
    16. public FileSystemTreeViewItem[] TreeViewItems
    17. {
    18. get
    19. {
    20. return treeViewItems;
    21. }
    22. private set
    23. {
    24. treeViewItems = value;
    25. if (PropertyChanged != null)
    26. PropertyChanged(this, new PropertyChangedEventArgs("TreeViewItems"));
    27. }
    28. }
    29. public event PropertyChangedEventHandler PropertyChanged;
    30. }
    Das ViewModel ist dem MainWindow bereits als DataContext zugewiesen.

    Mein Problem ist jetzt der XAML-Code. Alles, was ich bis jetzt geschafft habe, ist das hier:
    Spoiler anzeigen

    XML-Quellcode

    1. <Window x:Class="SimpleExplorer.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:local="clr-namespace:SimpleExplorer"
    5. Title="MainWindow" Height="350" Width="525"
    6. DataContext="{x:Static local:MainViewModel.Instance}">
    7. <Window.Resources>
    8. <HierarchicalDataTemplate x:Key="ExplorerTreeViewTemplate" DataType="{x:Type local:FileSystemTreeViewItem}" ItemsSource="{Binding ChildItems}">
    9. <StackPanel>
    10. <TextBlock Text="{Binding Directory.Name}"/>
    11. </StackPanel>
    12. </HierarchicalDataTemplate>
    13. </Window.Resources>
    14. <Grid>
    15. <Grid.ColumnDefinitions>
    16. <ColumnDefinition Width="*"/>
    17. <ColumnDefinition Width="*"/>
    18. </Grid.ColumnDefinitions>
    19. <TreeView Grid.Column="0" ItemTemplate="{StaticResource ExplorerTreeViewTemplate}" ItemsSource="{Binding TreeViewItems}">
    20. </TreeView>
    21. <ListView Grid.Column="1">
    22. </ListView>
    23. </Grid>
    24. </Window>

    So werden mir schonmal die Laufwerke angezeigt, auch mit dem Dummyknoten darin. Allerdings habe ich Probleme, darauf zu reagieren, wenn ein Knoten erweitert wurde. Ich möchte die Expanded-Eigenschaft jedes TreeViewItems an die Expanded-Eigenschaft des zugehörigen FileSystemTreeViewItem-Objekts binden, ich weiß aber nicht wie.
    Hat da jemand ne Idee? Kann doch eigentlich nicht so schwer sein.
    Binding-Picking im Xaml-Editor ist am Beispiel Directory-TreeView demonstriert.

    Viewmodels dazu sind viele verschiedene denkbar. Ich gebe mir Mühe, den belegten Speicher möglichst zu minimieren.
    Etwa, indem ich nicht in jedem Knoten ein BackingField hinterlege für die IsSelected-Property, sondern ich leite die IsSelected-Property auf ein Dictionary der selektierten Knoten, und wenn mein Knoten drinne ist, dann Is Selected.
    Inzwischen würde ich da noch viel weiter-konzipieren, etwa nicht-expandete Knoten wieder freigeben und Zeugs.
    Man muß aber auch überlegen, wozu mans braucht.
    Etwa ein DateiBrowser, der tagelang läuft, der würde sich mit der Zeit mit einer Riesen-Menge Knoten belasten. Hingegen bei sonem FolderBrowser-Dialog, der geöffnet, und dann wieder geschlossen wird - da isses eiglich nicht so wesentlich.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Danke vielmals.
    Ich musste einfach

    XML-Quellcode

    1. <TreeView.ItemContainerStyle>
    2. <Style TargetType="{x:Type TreeViewItem}">
    3. <Setter Property="IsExpanded" Value="{Binding Expanded, Mode=TwoWay}" />
    4. </Style>
    5. </TreeView.ItemContainerStyle>
    einfügen.