Wiedergabe von Ordnerstrukturen in WPF und C# mit MVVM

NewCommer

Neues Mitglied
Hallo zusammen 🖖,

ich arbeite derzeit an einer kleinen Büroverwaltungssoftware. Es soll eine Möglichkeit geben sich gewisse Ordnerstrukturen anzeigen zu lassen, ähnlich dem Windows Explorer. Mit SelectedItem soll es dann möglich sein, sich die Dateien z.B. Word oder PDF in einem extra Feld anzeigen zu lassen. Bis jetzt habe ich es geschafft, mir bestimmte Dateien aus einer Ordnerstruktur in einer Listbox zurück geben zu lassen. Dies reicht mir allerdings noch nicht, meine Vorstellung geht eher in Richtung des Explorers, wobei mir die Funktionalität in Ordner zu blättern und Dateien aufzurufen für ein vordefiniertes Stammverzeichnis ausreichen würden. Leider komme ich mit der TreeView nicht wirklich zurecht, alle bisherigen recherchen im Netz führten zu weniger hilfreichen ergebnissen. In den meisten Beispielen wird gezeigt wie man aus dem Codebehind Objecte abruft und diese dann mittels Binding anzeigen läßt. Da ich das MVVM Pattern umsetze, sind diese Beispiele für mich schlicht weg unbrauchbar. Mein Favorit ist derzeit die Idee eines DataProviders, wobei mir die Strukturierung des TreeViews und das Abrufen der Daten noch etwas zu schaffen macht. Da ich eine hierarchische Darstellung haben möchte, mit Images u.s.w., müsste ich mir für die Aufbereitung der Daten eine extra Klasse schaffen. Zumindest habe ich das aus dem Internet erfahren können. Hat eventuell jemand von euch Erfahrungen mit der Materie und kann mir da einen Schubs in die richtige Richtung geben? Bisher lege ich mir damit total die Karten.
 
Hi,

Sie können dies tun, ohne zusätzliche Komponenten zu verwenden. Dazu müssen Sie lediglich wissen, welche Datenstrukturen das TreeView-Element von WPF empfangen kann. Was wir dafür brauchen, ist ein eingebautes NET-Objekt, mit dem Sie meiner Meinung nach Erfahrung haben. Das von mir verwendete MVVM-Muster ist MvvMLight, jetzt "CommunityToolkit" genannt. Die folgenden Beispielcodes können Sie nach Belieben automatisieren und für Ihre File Explorer Teil nach Belieben verwenden.

Dies ist ein DataView Objekt.

Nachfolgend sende ich Ihnen ein Beispiel aus einem alten Projekt von mir. Dieser Code befindet sich in meiner Klasse "Constants.cs".

C#:
#region New Project Menu Nodes
public
static DataView MenuNodes {
  get {
    DataTable table = new DataTable("MainTemplates");

    //[B]Ich definiere vorab die Parameter, die in der Tabelle enthalten sein sollen.[/B]
    table.PrimaryKey = new System.Data.DataColumn[]{table.Columns.Add("ID", typeof(int))};
    table.Columns.Add("parentID", typeof(int));
    table.Columns.Add("MainTypes", typeof(string));
    table.Columns.Add("Templates", typeof(string));
    table.Columns.Add("SolutionTypes", typeof(string));
    table.Columns.Add("ProjectTypes", typeof(string));

    table.Rows.Add(0, null, null, null, null, "Installed");
    [B] //Main tree 1[/B]
        table.Rows.Add(1, null, null, null, null, "Online");
    [B] //Main Tree 2[/B]

        table.Rows.Add(2, 0, null, null, null, "Templates");
    [B] //Child Tree[/B]

        table.Rows.Add(3, 2, null, null, null, "System Project Types");
    [B] //child Tree element[/B]
        table.Rows.Add(4, 3, null, null, null, "Batch Project");
    [B] //child Tree element (parameter 3 zeigt System Project Types!)[/B]
        table.Rows.Add(5, 3, null, null, null, "DLL Support Project");
    [B] //child Tree element..[/B]
        table.Rows.Add(6, 3, null, null, null, "WMI Project Types");
    [B] //child Tree element..[/B]

        table.Rows.Add(7, 2, null, null, null, "Component Project Types");
    [B] //child Tree Erstellung (parameter 2 zeigt Templates !)[/B]
        table.Rows.Add(8, 7, null, null, null, "XML Projects");
    table.Rows.Add(9, 7, null, null, null, "WCF Service Project");
    table.Rows.Add(10, 7, null, null, null, "Windows Service Project");
    table.Rows.Add(11, 7, null, null, null, "Windows Task Project");
    table.Rows.Add(15, 7, null, null, null, "NHibernate Project");

    table.Rows.Add(12, 2, null, null, null, "Solution");
    table.Rows.Add(13, 12, null, null, null, "Batch Studio Solution");
    table.Rows.Add(14, 12, null, null, null, "Setup and Deployment");

    DataSet dtSet = new DataSet();
    dtSet.Tables.Add(table);

    [COLOR = rgb(184, 49, 47)][B] dtSet.Relations.Add("NewProjectMenu", table.Columns["ID"], table.Columns["parentID"]);
    [/ B][/ COLOR]

        return new DataView(table);
  }
}
#endregion



Der folgende Code befindet sich in meiner Klasse mit dem Namen "XViewModel.cs".

C#:
DataView _menuNodes; //[B] --> Klasse basierte definition.[/B]

public
DataView[B] RootNodes[/ B] {
  get {
    _menuNodes.RowFilter = "[parentID] is NULL";
    return _menuNodes;
  }
}

private
RelayCommand<DataRowView> _selectedProjectType;
/// <summary>
/// Gets the selected project type from "Create New Project".
/// </summary>
public
RelayCommand<DataRowView> SelectedProjectType {
  get { return _selectedProjectType; }
}

Der folgende Code befindet sich in mein Xaml mit dem Namen "XViewWindow.xaml".

Code:
i definition >> xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"


<TreeView Name="NewProjectTree" ItemsSource="{Binding RootNodes}" Grid.ColumnSpan="1" Grid.RowSpan="2" BorderBrush="Transparent">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectedItemChanged">
                    <i:InvokeCommandAction Command="{Binding Path=SelectedProjectType}" CommandParameter="{Binding ElementName=NewProjectTree, Path=SelectedItem}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding NewProjectMenu}">
                    <TextBlock Text="{Binding ProjectTypes}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>


Unten finden Sie einen Beispiel-Screenshot.



1708983214205.png

Ich habe nur versucht, eine allgemeine Vorstellung zu vermitteln. Sie kennen diese Struktur wahrscheinlich.



MfG / Best Regards
 
Zuletzt bearbeitet von einem Moderator:
Guten Abend, danke für ihre Antwort.

Ich bin meinem Ziel schon ein gutes Stück näher gekommen. Meine TreeView zeigt alle Ordner mit ihren enthaltenen Dateien an. Da ich allerdings Ordner und Dateien in meinem RootNode erwarte, habe ich in dieser Klasse 2 Collections implementiert. Wie bekommt man in der TreeView eine hierarchische Ordnerstruktur und die im RootNode enthaltenen Dateien angezeigt? Dazu müsste ich über ein Binding auf 2 verschiedene Collections in der RootNodeklasse zugreifen können.

Mein bisheriger Code ist bei Github hochgeladen -> https://github.com/Gewinde/OrdnerDurchlaufen
 
Hallo,

es tut mir leid, dass ich Ihnen spät antworte. Dies hängt davon ab, wie Sie die Datei-Anzeigemodule anzeigen möchten. Nehmen wir zum Beispiel an, wir wollen ein ordneranzeigesystem erstellen. Der wichtigste Punkt hierbei ist, dass Sie beim Öffnen des Programms nicht alle Ordner laden.

Wir haben eine TreeView-Struktur und wenn der Benutzer auf den gewünschten Knoten klickt, beginnt der Auflistungsprozess. Wir werden sie also nicht alle vorher in ein Objekt laden. Auf diese Weise belasten wir unsere Tabellenstruktur im Hintergrund nicht zusätzlich. Als ersten Schritt legen wir einen Startpunkt fest, denn wenn unsere Anwendung geöffnet wird, wird dies der Hauptordner der Suchen sein und der Benutzer kann diesen Ordner nach Belieben durchsuchen. Oder es kann in seinen Unterordnern navigieren. Genau wie Windows/Linux usw. Systeme. Obwohl es in diesem Beispiel so aussieht, als ob keine Last auf ein Objekt belastet wird, können Sie tatsächlich sehen, dass die Unterordner mit jedem Klick in einem Objekt gespeichert werden. Bei kleinen Prozess stellt dies möglicherweise kein Problem dar. Beim Einsatz in Systemen mit Millionen von Ordnersystemen weist es jedoch eine sehr schlechte Struktur auf.

Sie können die Codes, die ich unten vorbereitet habe, ausprobieren, um Ihnen eine Vorstellung von einer Konsolenanwendung zu geben.
(Das sind Codes, die in großer Eile geschrieben wurden. Bitte entschuldige mich ich, wenn die Code Fehler hat. Es gab bei mir keine Fehler, als ich es ein paar Mal ausführte.)

C#:
private
static DataTable folderTable;

static void Main(string[] args) {
  //SearchOption.TopDirectoryOnly : Weil ich nicht alle Verzeichnisse auf einmal mit AllDirectories Option laden möchte.
    foreach (string item in Directory.GetDirectories(@"C:\", "*", SearchOption.TopDirectoryOnly))
    {
    Console.WriteLine(item);
    }

    if (InitFolderStructure())
    {
    long id = 0;
    string mainPath = @"C:\"; string[] dirs = GetDeirectories(mainPath);
    folderTable.Rows.Add(id, id, Path.GetDirectoryName(mainPath), mainPath); //Wir sind in Zero, weil das Pfad C:\ ist oder haupt Node ist.

    if (dirs != null)
      ListDirectories(mainPath, 0);
    }

    Console.WriteLine();
    GetKey(0);

    Console.Read();
}

static void GetKey(long folderId) {
  long selectedIndex;
  bool exit = true;

  if (folderId != -1)
    Console.Write("Drücken Sie eine vorhandene Nummer, um die Verzeichnisse aufzulisten : ");
  else
    Console.Write("Bitte drücken Sie die existierte Nummer, um die Verzeichnisse aufzulisten : ");

  Console.WriteLine();

  while (exit)

  {
    selectedIndex = Convert.ToInt64(Console.ReadLine().Trim());

    if (folderTable.Rows.Contains(selectedIndex)) {
      string tmp = folderTable.Rows.Find(selectedIndex)[3].ToString(); //3 oder "folderPath"..
      ListDirectories(tmp, selectedIndex);
    } else {
      GetKey(-1);
    }
  }

  GetKey(folderId);
}

static void ListDirectories(string path, long folderId) {
  long ind = folderTable.Rows.Count;

  if (!string.IsNullOrEmpty(path)) {
    foreach (string item in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly)) {
      Console.WriteLine($ "{ind}  -) " + item);

      if (folderTable.Rows.Contains(ind + 1) == false)
        AddFolderToStructure(ind, folderId, Path.GetDirectoryName(item), item);

      ind++;
    }
    GetKey(folderId);
  } else
    GetKey(-1);
}

//Das Verzeichnislistensystem funktioniert hier durch die Aufzeichnung in DataTable.
//Natürlich hätte besserer Code geschrieben werden können. Ohne eine DataTable kann die Laufzeit beispielsweise fortfahren, indem sie nur eine Adresse vom Typ String behält.
static bool AddFolderToStructure(long id, long parentId, string folderName, string folderPath) {
  try {
    folderTable.Rows.Add(id, parentId, folderName, folderPath);

    //usw..

    return true;
  } catch (Exception xErr) {
    Console.WriteLine($ "Fehler : \n{xErr.Message}");
  }
  return false;
}

//Es handelt sich um eine modifizierte Version der DataTable-Struktur im Xaml-Beispiel, das ich zuvor gegeben habe.
static bool InitFolderStructure() {
  folderTable = new DataTable("FolderTemplates");

  //Sie können den long Type Object durch GUID ersetzen. Dies variiert je nach Planung Ihres Projekts.
  folderTable.PrimaryKey = new System.Data.DataColumn[]{folderTable.Columns.Add("ID", typeof(long))};

  //Sie können den long Type Object durch GUID ersetzen. Dies variiert je nach Planung Ihres Projekts.
  folderTable.Columns.Add("parentID", typeof(long));
  folderTable.Columns.Add("folderName", typeof(string));
  folderTable.Columns.Add("folderPath", typeof(string));

  //..usw zur Initialisierung

  return true;
}

MfG / Best Regards
 
Zuletzt bearbeitet von einem Moderator:
Zurück
Oben Unten