(C#.NET) - Listowanie plików z katalogu i subkatalogów do ListBox


(1niez) #1

Cześć wszystkim!

Wie ktoś jak można wylistować do listboxa w c#.net wszystkie pliki znajdujące się w wybranym folderze i w folderach znajdujących się w nim? Chodzi mi o pozyskanie takiego efektu mniej więcej w listbox:
“C:\Folder\1.txt
C:\Folder\Subfolder\2.txt”

Od razu zaznaczam, że nie będą wskazane nazwy subfolderów przez użytkownika i program musi z nich wyłapać sam pliki. Nie interesują mnie same ścieżki katalogów tylko plików w głównym folderze i subfolderach.

Pozdrawiam!


(kowgli) #2

Generalnie pomocna będzie tu klasa DirectoryInfo

Konkretnie jej statyczne metody:
DirectoryInfo.GetFiles
i
DirectoryInfo.GetDirectories

Tego typu struktury drzewiaste przechodzi się rekurencyjnie. Tzn. deklarujesz jakąś listę, którą będziesz wypełniał. Zaczynasz z samej góry (“C:”), do listy wrzucasz wszystkie pliki. Później uruchamiasz tę samą funkcję dla wszystkich podkatalogów katalogu w którym jesteś.

Listę, którą wypełniasz deklarujesz jako zmienną na poziomie klasy (wtedy musisz pamiętać o jej wyczyszczeniu przed rozpoczęciem rekursji) lub lepiej przekazujesz (jako referencję) do każdego wywołania.
Mógłbyś się pokusić o wersję wielowątkową np. z użyciem Parallel.Foreach na podkatalogach, ale wtedy musisz użyć np. ConcurrentBag<T>

Pokazanie tego na ekranie np. w listboxie jest rzeczą wtórną i banalną. Nie ma wiele wspólnego z samym pobraniem plików.

Skanowanie całego dysku w ten sposób może chwilę zająć. To mogą być spokojnie dziesiątki milionów plików.


(1niez) #3

Dzięki! Sporo to pomogło. :smiley: Jednak mam teraz drobny problem. Gdy listuję sobie pliki za pomocą tego kodu:

   foreach (var location in listBox1.Items)
            {
                string loc = Convert.ToString(location);
          
                    listBox3.Items.Add(Directory.GetFiles(loc, "*.*", SearchOption.AllDirectories));
    
            }

To wywala mi błąd, że do niektórych katalogów np. w C:\Recycle Bin\ nie może się dostać co oczywiście mi nie przeszkadzało i chciałem to zignorować przez użycie catch. Tylko jak użyłem catch to już w ogóle nie listowało. Catcha użyłem tak:

   foreach (var location in listBox1.Items)
            {
                string loc = Convert.ToString(location);
                try
                {
                    listBox3.Items.Add(Directory.GetFiles(loc, "*.*", SearchOption.AllDirectories));
                }
                catch
                {

                }
            }

(kowgli) #4

Zmień
catch {}

na

catch(Exception ex) {} i postaw tam breakpointa, dowiesz się w czym problem

  1. Nie wrzucaj elementów bezpośrednio do listboxa. Wypełnij jakąś kolekcję w pamięci i jak będzie gotowa ustaw ją jako DataSource.

(1niez) #5

Okej, zastosowałem się do Twoich uwag. Ogólnie metoda Try i Catch w ogóle nie działają teraz pod pętlą each bo ani się nie wypełnia listbox danymi ani catch nie reaguje.


(kowgli) #6

Proszę prosty przykład jak można to zrobić:


(xBotekx) #7

Rozwiązanie rekurencyjne:

    private List<String> DirSearch(string sDir)
       {
       List<String> files = new List<String>();
    try
    {
        foreach (string f in Directory.GetFiles(sDir))
        {
            files.Add(f);
        }
        foreach (string d in Directory.GetDirectories(sDir))
        {
            files.AddRange(DirSearch(d));
        }
    }
    catch (System.Exception excpt)
    {
        MessageBox.Show(excpt.Message);
    }

    return files;
    }

Rozwiązanie zwykłe:

public static IEnumerable<string> GetFileList(string fileSearchPattern, string rootFolderPath)
{
    Queue<string> pending = new Queue<string>();
    pending.Enqueue(rootFolderPath);
    string[] tmp;
    while (pending.Count > 0)
    {
        rootFolderPath = pending.Dequeue();
        try
        {
            tmp = Directory.GetFiles(rootFolderPath, fileSearchPattern);
        }
        catch (UnauthorizedAccessException)
        {
            continue;
        }
        for (int i = 0; i < tmp.Length; i++)
        {
            yield return tmp[i];
        }
        tmp = Directory.GetDirectories(rootFolderPath);
        for (int i = 0; i < tmp.Length; i++)
        {
            pending.Enqueue(tmp[i]);
        }
    }
}

Co prawda kod nie wrzuca do listboxa ale z tym już sobie poradzisz chyba. Kody nie są moje pochodzą z internetu ale osobiście polecam drugi. Nie powinien mieć kłopotu z nieautoryzowanym dostępem


(1niez) #8

Dzięki! Metoda druga jest super! :smiley: