文件列表小部件

File list widget

我想要一个列表小部件,它是单个目录中文件的平面视图。它应该被实时监控,理想情况下,如果短时间内要发生很多事情,我应该能够暂时禁用它自我更新。是否有一个标准的小部件可以做到这一点?快速搜索会出现 wxGenericDirCtrl 但是,据我所知,它无法只显示单个目录的文件(而不是目录)。它更像是一棵树而不是一个列表。

否则,制作类似内容的最佳方法是什么。我可能可以子类化 wxListBox 并使用 wxFileSystemWatcher 来保持视图同步,但这会很乏味。有没有更好的方法?

我在Windows;便携性不是问题。我的目标是 Windows 7 到 10。

正如 Igor 上面所说,鉴于您提出的要求,我认为除了使用 wxFileSystemWatcher.

之外还有其他方法可以做到这一点

我整理了一个示例,说明如何根据文件系统观察器的报告来更新列表框。我不得不承认这确实比我想象的要复杂,但我不认为它过于乏味。

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include <wx/fswatcher.h>
#include <wx/filepicker.h>
#include <vector>

class MyFrame: public wxFrame
{
    public:
        MyFrame();
    private:
        // Event handlers
        void OnWatcher(wxFileSystemWatcherEvent& event);
        void OnDirChanged(wxFileDirPickerEvent& event);

        // Helper functions
        wxString StripPath(const wxString&);
        void DeleteFromListbox(const wxString&);

        wxFileSystemWatcher m_watcher;
        wxListBox* m_listBox;
};

MyFrame::MyFrame()
        :wxFrame(NULL, wxID_ANY, "Filesystem watcher frame", wxDefaultPosition,
                 wxSize(600, 400))
{
    // Set up the UI elements.
    wxPanel* mainPanel = new wxPanel(this,wxID_ANY);
    wxDirPickerCtrl* dirPicker = new wxDirPickerCtrl(mainPanel, wxID_ANY);
    m_listBox = new wxListBox(mainPanel, wxID_ANY);

    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(dirPicker, wxSizerFlags(0).Expand().Border(wxALL));
    sizer->Add(m_listBox, wxSizerFlags(1).Expand().Border(wxALL&~wxTOP));

    mainPanel->SetSizer(sizer);
    Layout();

    // Set up the event handlers.
    m_watcher.SetOwner(this);
    dirPicker->Bind(wxEVT_DIRPICKER_CHANGED, &MyFrame::OnDirChanged, this);
    Bind(wxEVT_FSWATCHER, &MyFrame::OnWatcher, this);
}

void MyFrame::OnWatcher(wxFileSystemWatcherEvent& event)
{
    wxFileName name = event.GetPath();

    // Ignore all watch events for folders.
    if ( name.IsDir() )
    {
        return;
    }

    wxFileName newName = event.GetNewPath();
    int changeType = event.GetChangeType();

    switch ( changeType )
    {
        case wxFSW_EVENT_CREATE :
            m_listBox->Append( StripPath(name.GetFullPath()) );
            break;
        case wxFSW_EVENT_DELETE :
            DeleteFromListbox( StripPath(name.GetFullPath()) );
            break;
        case wxFSW_EVENT_RENAME :
            DeleteFromListbox( StripPath(name.GetFullPath()) );
            m_listBox->Append( StripPath(newName.GetFullPath()) );
            break;
        case wxFSW_EVENT_MODIFY :
            wxFALLTHROUGH;
        case wxFSW_EVENT_ACCESS :
            wxFALLTHROUGH;
        case wxFSW_EVENT_ATTRIB :
            wxFALLTHROUGH;
#ifndef __WINDOWS__
        case wxFSW_EVENT_UNMOUNT :
            wxFALLTHROUGH;
#endif // __WINDOWS__
        case wxFSW_EVENT_WARNING :
            wxFALLTHROUGH;
        case wxFSW_EVENT_ERROR  :
            wxFALLTHROUGH;
        default:
            break;
    }
}

void MyFrame::OnDirChanged(wxFileDirPickerEvent& event)
{
    if ( !::wxDirExists(event.GetPath()) )
    {
        return;
    }
    
    // Remove the current folder from the watch and add the new one.
    m_watcher.RemoveAll();

    wxString pathWithSep = event.GetPath();
    if ( pathWithSep.Right(1) != wxFileName::GetPathSeparator() )
    {
        pathWithSep << wxFileName::GetPathSeparator();
    }
    m_watcher.Add(wxFileName(pathWithSep));

    // Get a list of the files in new folder.
    std::vector<wxString> files;
    wxDir dir(pathWithSep);
    wxString curName;
    if ( dir.GetFirst(&curName, "*", wxDIR_FILES) )
    {
        files.push_back(StripPath(curName));
    }
    while ( dir.GetNext(&curName) )
    {
        files.push_back(StripPath(curName));
    }

    // Replace the current contents of the listbox with the new filenames.
    m_listBox->Clear();
    for ( auto it = files.begin() ; it != files.end() ; ++it )
    {
        m_listBox->Append(*it);
    }
}

wxString MyFrame::StripPath(const wxString& name)
{
    wxFileName fn(name);
    return fn.GetFullName();
}

void MyFrame::DeleteFromListbox(const wxString& name)
{
    int index = m_listBox->FindString(name);
    if ( index != wxNOT_FOUND )
    {
        m_listBox->Delete(static_cast<unsigned int>(index));
    }
}

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            MyFrame* frame = new MyFrame();
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);

这只是一个简单的例子。还有一些改进的余地:

  1. 我不确定如果在枚举过程中对文件夹中的文件进行更改会发生什么情况。如果这是一个问题,我想你可以在枚举期间收集观察者事件,然后在完成后解决它们。
  2. 如果枚举的文件夹可以包含很多文件,最好在辅助线程中进行枚举,
  3. 如果您想显示图标或元数据,列表控件可能比列表框更合适。列表控件还允许对列表进行排序。