Currently, I am using wxWidgets' wxGenericDirCtrl, which gives me a user interface to the directory tree. It looks like this:
However, I would prefer it to look like this:
Talking with the wxWidgets developers, they suggest that one good way to proceed is to modify the wxGenericDirCtrl class to use the native Windows directory control. I would like to contribute this functionality to wxWidgets myself, but I'm not sure where to start, and could use some suggestions to get me started.
Questions:
- What's the correct native way to create such a control in C++. Do I use MFC, .NET, or something else?
- Are there any keywords which can help me find this information?
- Is there any example code showing how to do this?
Creating a Window Explorer like directory tree (known as "shell namespace tree control") has become quite easy since Windows Vista. This control can be created through a COM class called INameSpaceTreeControl
. Windows 7 added a newer version of this class called INameSpaceTreeControl2
.
The following sample code could be called from the WM_CREATE
handler of the parent window. It creates a namespace tree control that has its root set to the desktop folder. Other root folders are possible, even multiple roots can be inserted.
#include <ShlObj.h> //Shell COM API
#include <atlbase.h> //CComPtr
static CComPtr<INameSpaceTreeControl> pTree;
if( SUCCEEDED( pTree.CoCreateInstance( CLSID_NamespaceTreeControl ) ) )
{
RECT rc{ 0, 0, 444, 333 }; // Client coordinates of the tree control
if( SUCCEEDED( pTree->Initialize( hWndParent, &rc,
NSTCS_HASEXPANDOS | NSTCS_AUTOHSCROLL | NSTCS_FADEINOUTEXPANDOS ) ) )
{
CComPtr<IShellItem> pItem;
if( SUCCEEDED( SHCreateItemInKnownFolder( FOLDERID_Desktop, 0, nullptr,
IID_PPV_ARGS( &pItem ) ) ) )
{
pTree->AppendRoot( pItem, SHCONTF_FOLDERS, NSTCRS_EXPANDED, nullptr );
}
}
}
Destroy the namespace tree control by calling the Release()
method of the COM object when the parent window gets destroyed, typically in the WM_DESTROY
handler of the parent window:
pTree.Release(); // Releases the COM object and sets the pointer to nullptr
Don't forget to CoInitialize(nullptr)
once at startup of your program and CoUninitialize()
before shutdown.
wxWidgets does NOT use MFC, .NET or whatever other high-level library on Windows. Just the basic system API (libs kernel32, winspool, comctl32, etc). Same goes for OSX, using the OS provided API. In Linux there's no such "basic API", and thus wxWidgets uses GTK+.
You already have the keywords to search for: explorer like control.
wxGenericDirCtrl
has GetTreeCtrl()
member. Then, just add images to nodes, for example by wxTreeCtrl::AssignButtonsImageList()
. See wx docs
ExplorerApp.h
#ifndef EXPLORERAPP_H
#define EXPLORERAPP_H
#include <wx/app.h>
class ExplorerApp : public wxApp
{
public:
virtual bool OnInit();
};
#endif // EXPLORERAPP_H
ExplorerApp.cpp
#include "ExplorerApp.h"
//(*AppHeaders
#include "ExplorerMain.h"
#include <wx/image.h>
//*)
IMPLEMENT_APP(ExplorerApp);
bool ExplorerApp::OnInit()
{
//(*AppInitialize
bool wxsOK = true;
wxInitAllImageHandlers();
if ( wxsOK )
{
ExplorerFrame* Frame = new ExplorerFrame(0);
Frame->Show();
SetTopWindow(Frame);
}
//*)
return wxsOK;
}
ExplorerMain.h
#ifndef EXPLORERMAIN_H
#define EXPLORERMAIN_H
#include<wx/imaglist.h>
#include<wx/artprov.h>
#include<wx/dir.h>
////(*Headers(ExplorerFrame)
#include <wx/treectrl.h>
#include <wx/textctrl.h>
#include <wx/button.h>
#include <wx/dirdlg.h>
#include <wx/frame.h>
//*)
class ExplorerFrame: public wxFrame
{
public:
ExplorerFrame(wxWindow* parent,wxWindowID id = -1);
virtual ~ExplorerFrame();
private:
////(*Handlers(ExplorerFrame)
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnButton1Click(wxCommandEvent& event);
void findir(wxTreeItemId,wxString);
//*)
////(*Identifiers(ExplorerFrame)
static const long ID_TEXTCTRL1;
static const long ID_BUTTON1;
static const long ID_CUSTOM1;
//*)
////(*Declarations(ExplorerFrame)
wxButton* Button1;
wxDirDialog* DirDialog1;
wxTextCtrl* TextCtrl1;
wxTreeCtrl* Mytreectrl;
//*)
wxImageList* myimageliste;
wxTreeItemId root;
wxTreeItemId Child1;
wxTreeItemId Child2;
wxTreeItemId Child3;
DECLARE_EVENT_TABLE()
};
#endif // EXPLORERMAIN_H
ExplorerMain.cpp
#include "ExplorerMain.h"
#include <wx/msgdlg.h>
////(*InternalHeaders(ExplorerFrame)
#include <wx/intl.h>
#include <wx/string.h>
//*)
//helper functions
enum wxbuildinfoformat {
short_f, long_f };
wxString wxbuildinfo(wxbuildinfoformat format)
{
wxString wxbuild(wxVERSION_STRING);
if (format == long_f )
{
#if defined(__WXMSW__)
wxbuild << _T("-Windows");
#elif defined(__UNIX__)
wxbuild << _T("-Linux");
#endif
#if wxUSE_UNICODE
wxbuild << _T("-Unicode build");
#else
wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
}
return wxbuild;
}
//(*IdInit(ExplorerFrame)
const long ExplorerFrame::ID_TEXTCTRL1 = wxNewId();
const long ExplorerFrame::ID_BUTTON1 = wxNewId();
const long ExplorerFrame::ID_CUSTOM1 = wxNewId();
//*)
BEGIN_EVENT_TABLE(ExplorerFrame,wxFrame)
//(*EventTable(ExplorerFrame)
//*)
END_EVENT_TABLE()
ExplorerFrame::ExplorerFrame(wxWindow* parent,wxWindowID id)
{
////(*Initialize(ExplorerFrame)
Create(parent, id, _("wxTreeCtrl"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
SetClientSize(wxSize(400,298));
TextCtrl1 = new wxTextCtrl(this, ID_TEXTCTRL1, _("C:/"), wxPoint(32,16), wxSize(240,22), 0, wxDefaultValidator, _T("ID_TEXTCTRL1"));
Button1 = new wxButton(this, ID_BUTTON1, _("Browse"), wxPoint(320,16), wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
Mytreectrl = new wxTreeCtrl(this,ID_CUSTOM1,wxPoint(24,56),wxSize(352,224),wxTR_HAS_BUTTONS,wxDefaultValidator,_T("ID_CUSTOM1"));
DirDialog1 = new wxDirDialog(this, _("Select directory"), wxEmptyString, wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, _T("wxDirDialog"));
Center();
Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ExplorerFrame::OnButton1Click);
//*)
myimageliste = new wxImageList(16,16);
myimageliste->Add(wxArtProvider::GetBitmap(wxART_FOLDER,wxART_OTHER,wxSize(16,16)));
myimageliste->Add(wxArtProvider::GetBitmap(wxART_REPORT_VIEW,wxART_OTHER,wxSize(16,16)));
Mytreectrl->AssignImageList(myimageliste);
root = Mytreectrl->AddRoot(_T("Root"),0);
/*Child1 = Mytreectrl->AppendItem(root,_T("Folder 1"),0);
Mytreectrl->AppendItem(Child1,_T("My folder 11"),1);
Mytreectrl->AppendItem(Child1,_T("My folder 12"),1);
Mytreectrl->AppendItem(Child1,_T("My folder 13"),1);
Child2 = Mytreectrl->AppendItem(root,_T("Folder 2"),0);
Mytreectrl->AppendItem(Child2,_T("My folder 21"),1);
Mytreectrl->AppendItem(Child2,_T("My folder 22"),1);
Mytreectrl->AppendItem(Child2,_T("My folder 23"),1);
Child3 = Mytreectrl->AppendItem(root,_T("Folder 3"),0);
Mytreectrl->ExpandAll();*/
//findir(root,"C:\\");
}
ExplorerFrame::~ExplorerFrame()
{
//(*Destroy(ExplorerFrame)
//*)
}
void ExplorerFrame::OnQuit(wxCommandEvent& event)
{
Close();
}
void ExplorerFrame::OnAbout(wxCommandEvent& event)
{
wxString msg = wxbuildinfo(long_f);
wxMessageBox(msg, _("Welcome to..."));
}
void ExplorerFrame::findir(wxTreeItemId id,wxString path){
wxDir mydir;
//DirDialog1->SetPath(_T("C://"));
bool isopen = false;
//int dir = DirDialog1->ShowModal();
// if(dir == wxID_OK){
//TextCtrl1->SetValue(DirDialog1->GetPath());
//Mytreectrl->DeleteChildren(root);
//Mytreectrl->SetItemText(root,DirDialog1->GetPath());
isopen = mydir.Open(path);
//}
wxString filename;
bool cont = false;
bool con = false;
if(isopen){
con = mydir.GetFirst(&filename,wxEmptyString,wxDIR_DIRS);
}
int handleforfiles = 0;
while(con and isopen){
wxTreeItemId ids = Mytreectrl->AppendItem(id,filename,0);
//wxMessageBox(path+filename,path+'\\'+filename);
if(handleforfiles >= 150){
//wxMessageBox(_T("The Max File limit reached"),_T("The Max File limit reached"));
break;
}
findir(ids,path+'\\'+filename);
con = mydir.GetNext(&filename);
++handleforfiles;
}
if(isopen){
cont = mydir.GetFirst(&filename,wxEmptyString,wxDIR_FILES);
}
handleforfiles = 0;
while(cont and isopen){
Mytreectrl->AppendItem(id,filename,1);
if(handleforfiles >= 150){
//wxMessageBox(_T("The Max File limit reached"),_T("The Max File limit reached"));
break;
}
cont = mydir.GetNext(&filename);
++handleforfiles;
}
}
void ExplorerFrame::OnButton1Click(wxCommandEvent& event)
{
//wxDir mydir;
DirDialog1->SetPath(_T("C://"));
//bool isopen = false;
int dir = DirDialog1->ShowModal();
if(dir == wxID_OK){
TextCtrl1->SetValue(DirDialog1->GetPath());
Mytreectrl->DeleteChildren(root);
Mytreectrl->SetItemText(root,DirDialog1->GetPath());
//isopen = mydir.Open(DirDialog1->GetPath());
findir(root,DirDialog1->GetPath());
}
}
来源:https://stackoverflow.com/questions/48586051/creating-a-windows-explorer-like-directory-tree