filtered file path list treeview in wpf

Viewed 35

I works in c# and have a class that fills a TreeView in Wpf, my problems is that i can't filter del path depending on the elements, I enter a textbox

This is how it currently works for me

This is how it currently works for me

And this is how I need it to come out when entering the value in the textbox

And this is how I need it to come out when entering the value in the textbox

public class ItemProvider
{
    public List<Item> GetItems(string path)
    {
        var items = new List<Item>();
        var dirInfo = new DirectoryInfo(path);

        foreach (var directory in dirInfo.GetDirectories())
        {
            var item = new DirectoryItem
            {
                Name = directory.Name,
                Path = directory.FullName,
                Items = GetItems(directory.FullName)
            };

            items.Add(item);
        }

        foreach (var file in dirInfo.GetFiles())
        {
            var item = new FileItem
            {
                Name = file.Name,
                Path = file.FullName
            };

            items.Add(item);
        }

        return items;
    }
}
1 Answers

This is how I'd implement this. (Actually, implemented for this answer.)

I'm using the CommunityToolkit.Mvvm NuGet package to implement the MVVM design.

ItemProvider.cs

using System.Collections.Generic;

namespace WpfAppTreeViewTest;

public class Item
{
    public string Name { get; set; }
    public string Path { get; set; }
}

public class FileItem : Item
{
}

public class DirectoryItem : Item
{
    public List<Item> Items { get; set; }

    public DirectoryItem()
    {
        Items = new List<Item>();
    }
}

public class ItemProvider
{
    public List<Item> GetItems(string path)
    {
        return new List<Item>()
        {
            new DirectoryItem()
            {
                Name = "comedy",
                Items = new List<Item>()
                {
                    new FileItem() { Name = "1001.PDF" },
                    new FileItem() { Name = "1002.PDF" },
                    new FileItem() { Name = "1003.PDF" },
                },
            },
            new DirectoryItem()
            {
                Name = "horror",
                Items = new List<Item>()
                {
                    new FileItem() { Name = "1005.PDF" },
                    new FileItem() { Name = "1006.PDF" },
                },
            },
            new DirectoryItem()
            {
                Name = "suspense",
                Items = new List<Item>()
                {
                    new FileItem() { Name = "1002.PDF" },
                    new FileItem() { Name = "1005.PDF" },
                    new FileItem() { Name = "1006.PDF" },
                },
            },
        };
    }
}

MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.Generic;
using System.Linq;

namespace WpfAppTreeViewTest;
// This class needs to be partial for the CommunityToolkit's source generators.
public partial class MainWindowViewModel : ObservableObject
{
    // The source generators will auto-create a "FilterText" property.
    [ObservableProperty]
    private string filterText = string.Empty;

    // The source generators will auto-create a "FilteredItems" property.
    [ObservableProperty]
    private IEnumerable<Item>? filteredItems;

    private List<Item>? items;

    public MainWindowViewModel()
    {
        var itemProvider = new ItemProvider();
        this.items = itemProvider.GetItems("");
        FilteredItems = this.items;
    }

    // This is a "partial" method created by the source generator.
    // This method will be called everytime "FilterText" changes.
    partial void OnFilterTextChanged(string value)
    {
        FilteredItems = this.items?
            .OfType<DirectoryItem>()
            .Select(d => new DirectoryItem()
            {
                Name = d.Name,
                Path = d.Path,
                Items = d.Items.Where(x => x.Name.Contains(value)).ToList(),
            })
            .Where(x => x.Items.Any() is true);
    }
}

MainWindow.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfAppTreeViewTest;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public MainWindowViewModel ViewModel { get; } = new();
}

MainWindow.xaml

<Window
    x:Class="WpfAppTreeViewTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfAppTreeViewTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="ThisWindow"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <StackPanel>
        <StackPanel.Resources>
            <HierarchicalDataTemplate
                DataType="{x:Type local:DirectoryItem}"
                ItemsSource="{Binding Items}">
                <TextBlock
                    Text="{Binding Path=Name}"
                    ToolTip="{Binding Path=Path}" />
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type local:FileItem}">
                <TextBlock
                    Text="{Binding Path=Name}"
                    ToolTip="{Binding Path=Path}" />
            </DataTemplate>
        </StackPanel.Resources>
        <TextBox Text="{Binding ElementName=ThisWindow, Path=ViewModel.FilterText, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
        <TreeView ItemsSource="{Binding ElementName=ThisWindow, Path=ViewModel.FilteredItems}">
            <TreeView.Resources>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsExpanded" Value="True" />
                </Style>
            </TreeView.Resources>
        </TreeView>
    </StackPanel>
</Window>
Related