Click here to Skip to main content
Click here to Skip to main content

Resizing subclassed CFileDialog and adding a 'Select All' button

, 1 Jan 2002 CPOL
Rate this:
Please Sign up or sign in to vote.
This article shows how to subclass the CFileDialog dialog and add a 'Select All' button

Resized subclassed CFileDialog with added 'Select All' button

Introduction

This article will demonstrate how to resize the instance of a subclassed CFileDialog and then add a 'SelectAll' button that will select all files (but not folders) in the currently displayed folder. The article assumes that you already know how to subclass MFC provided classes and deal with the associated issues. We therefore will concentrate on how to resize the dialog and re-layout the controls on it (so that they properly cover the enlarged dialog window area). Then, we will show how to add the button, capture the user clicks on it and select all currently displayed files in response.

What needs to be done

What we are trying to accomplish is the following

  • Resize our CFileDialog derived dialog instance
  • Rearrange the existing controls on our dialog according to the new dialog window size
  • Add a 'Select All' button control at the bottom of the dialog
  • Capture user clicks on that button
  • Respond to such clicks by selecting all currently displayed files in the file browser control

As you will see the last task is the hardest.

In general, when implementing a custom dialog based on an existing MFC common dialog there are two basic ways to go about it:

  • Import the common dialog template as a dialog resource and use the resource editor and class wizard in customizing the dialog and adding functionality.
  • Directly subclass from the common dialog class and deal with various added issues.

The second approach (which is the one demonstrated in this article) has the added advantage that if the common dialog class changes in the future, your applications gets the advantages of the new class without any extra work.

The issues

In resizing a subclassed CFileDialog instance and adding a button to it that will select all files currently displayed there are 3 major issues that need to be resolved:

  1. Accessing each of the various controls on the dialog and moving/resizing them appropriately.
  2. Obtaining the IShellBrowser interface of the dialog's folder view control
  3. Obtaining the full path of the currently displayed folder

Issue 1

The resource names of the controls in the CFileDialog can be found in the Dlgs.h file that resides in the same directory as the common dialog templates (i.e. The 'Include' directory of your VC++ CD or Platform SDK CD). You need to import "Dlgs.h" into your project in order to be able to access the controls in the dialog window. In the "open file" dialog variant shown in the sample picture above there are 6 controls:

  • stc3 and stc2 are the two label ctrls
  • edt1 and cmb1 are the lower edit and combo ctrls
  • IDOK and IDCANCEL are the dialog buttons
  • lst1 is the file browser control (that also owns the upper edit and combo style controls)

Using these names and GetDlgCtrl() calls on the parent window you can obtain pointers to the controls.

Issue 2

The lst1 control of the dialog cannot be treated as a CListCtrl control because it simply is not although it looks like one and is declared as one in the CFileDialog template (and this is why attempts to, for instance, get the list count of the control in this way will always result to the value of zero). This control is really a file browser (or folder view control) just like the one our well known file explorer is built around.

Obtaining the IShellBrowser COM interface of the control is not straight forward (or at least I could not find a more straight forward way than the one I finally used). I initially tried to get the IUnknown interface using a pointer to the control but the interface simply could not get fetched this way.

Apparently, the only way to get the IShellBrowser interface is to send the undocumented WM_GETISHELLBROWSER message to the dialog's parent. WM_GETISHELLBROWSER should be explicitly defined as WM_USER + 7 since it is not yet defined in the standard header files. Here is a snippet of the code doing that:

#define WM_GETISHELLBROWSER (WM_USER + 7)


IShellBrowser* pShBrowser =
  (IShellBrowser*)::SendMessage( p->m_hWnd, WM_GETISHELLBROWSER, 0, 0 );

Issue 3

In order to get the full path of the currently displayed dialog we again need to send a message to the dialog's parent. This is the CDM_GETFOLDERPATH message. Assuming that p is a pointer to the dialog's parent (obtained through a GetParent() call), here is the code that would do just that:

char* path = (char*) malloc(260);
 
p->SendMessage(CDM_GETFOLDERPATH, 260, (LPARAM) path);

Other Tasks

In order to capture the extra button's clicks I have subclassed my button from CButton and overridden its OnLButtonDown() method. Of course the relevant entry should be added to the message map of the button class:

BEGIN_MESSAGE_MAP(MyCButton, CButton)
  ON_WM_LBUTTONDOWN( )
END_MESSAGE_MAP()


Also note that in order to be able to select multiple files in a CFileDialog derived dialog the underlying CFileDialog should be initialized with the OFN_ALLOWMULTISELECT flag set.

Code

The code listing only includes the basic and important portions of the code. This is not a full application.

The OnInitDialog() override

The first part is the listing of the subclassed (from CFileDialog) dialog's overridden OnInitDialog() method. We use this method to resize the dialog's window and then rearrange the position or size of the contained controls. Also, it is here that we initialize and add the extra button, which is also a member of our 'file open' dialog class (but does not really need to be).

/////////////////////////////////////////////////////////////////////////////////
BOOL MyFileDialog::OnInitDialog()
{
  CFileDialog::OnInitDialog(); //Call base class method first

  UINT AddSize = 300; //Indicates how much bigger
                      //the dialog window will be

  const UINT cNum = 7; //Number of controls 
                       //in subclassed CFileDialog

  
  CWnd *pW = GetParent(); // Get a pointer to the parent window.

  RECT Rect;
  pW->GetWindowRect(&Rect); //Get rect of parent window
  
  // Change the size of parent window according to 
  pW->SetWindowPos( NULL, 0, 0, 
                    Rect.right - Rect.left, 
                    Rect.bottom - Rect.top + AddSize, 
                    SWP_NOMOVE );

  //Create an array that contains the control names 
  int Control[cNum] = { stc3, stc2,     //The two label ctrls
                        edt1, cmb1,     //Edit and combo ctrls
                        IDOK, IDCANCEL, //The dialog buttons 
                        lst1 };         //The Explorer window

  
  CWnd *c;    //c will hold the address of each control in the dialog
  RECT cRect; //The control's rect (coords)

  //Move all controls to a compensate for the new parent size
  for (int i = 0 ; i < cNum ; i++)
  {
    c = pW->GetDlgItem(Control[i]); //Fetch control

    c->GetWindowRect(&cRect);   //Get control's rect
    pW->ScreenToClient(&cRect); //Convert to child window coords

    if (Control[i] != lst1) //For any control except the lst1
                            //move the control appropriately
      c->SetWindowPos( NULL, 
                       cRect.left, cRect.top + AddSize,
                       0, 0, SWP_NOSIZE );
    
    else //size the folder view control appropriately
      c->SetWindowPos( NULL, 0, 0,
                       cRect.right  - cRect.left, 
                       cRect.bottom - cRect.top + AddSize, 
                       SWP_NOMOVE );
  }

  //If this is a multi-select dialog
  //then create the 'Select All' button
  if (this->m_ofn.Flags & OFN_ALLOWMULTISELECT)                   
  {    
    pW->GetWindowRect(&Rect);  //Get latest parent rect
    pW->ScreenToClient(&Rect); //Make rect values relative
                               //to parent window
    
    //Position the 'Select All' button
    RECT bRect;
      bRect.bottom = Rect.bottom - 8;
      bRect.top    = Rect.bottom - 30;
      bRect.left   = Rect.left + 8;
      bRect.right  = Rect.left + 102;

    //Create new subclassed CButton
    //holding pointer to parent window
    SelAll = new MyCButton(pW);

    //Create() requires relative rect struct for child window creation
    SelAll->Create("Select All", BS_PUSHBUTTON, bRect, pW, 4711);
    SelAll->ShowWindow(SW_SHOW);  
  }
  
  return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////

The OnLButtonDown() override

Here is the code listing for the CButton derived class' overridden OnLButtonDown() method:

/////////////////////////////////////////////////////////////////////////////////
afx_msg void MyCButton::OnLButtonDown( UINT nFlags, CPoint point )
{
  //call base class method first
  CButton::OnLButtonDown(nFlags, point); 

  //Get the full path of the currently displayed folder
  //in the CFileDialog subclass instance. Member var p
  //holds the address of the dialog instance
  char* path = (char*) malloc(260);
  p->SendMessage(CDM_GETFOLDERPATH, 260, (LPARAM) path);

  CString fdpath = path; //Hold the full path in a CString

  //Copy the CString contents in a system string
  LPOLESTR ofdpath = fdpath.AllocSysString();

  
  LPMALLOC pMalloc = NULL;

  ::SHGetMalloc(&pMalloc); //Get pointer to shell alloc


  LPITEMIDLIST pidFile   = NULL; //Will hold single item 
                                 //PIDL to a file
  
  LPITEMIDLIST pidFolder = NULL; //Will hold full PIDL
                                 //to the current folder
    
  
  IShellFolder *sfRoot   = NULL; //Interface to shell space root
  IShellFolder *sfFolder = NULL; //Interface to current folder
  

  ::SHGetDesktopFolder(&sfRoot); //Get shell namespace's root

  //Get PIDL of current folder by parsing its full path
  sfRoot->ParseDisplayName(NULL, NULL, ofdpath, NULL, &pidFolder, NULL);
  
  //Since we now have the current folder's PIDL
  //we can bind it's IShellFolder Interface to
  //our sfFolder pointer
  sfRoot->BindToObject(pidFolder, NULL, IID_IShellFolder, (void**) &sfFolder);



  //We now need to get a hold of the IShellBrowser interface as exposed
  //by the subclassed CFileDialog instance's folder view COM server.
  //The easiest way is to send th undocumended WM_GETISHELLBROWSER message
  //(See Q157247 in MS knowledge base)
  IShellBrowser* pShBrowser =
    (IShellBrowser*)::SendMessage( p->m_hWnd, WM_GETISHELLBROWSER, 0, 0 );


  //Get the current view's IShellView interface
  //from the IShellBrowser interface
  IShellView* pShView = NULL;
  pShBrowser->QueryActiveShellView( &pShView );

  LPENUMIDLIST pidList; //PIDL enumeration that will hold
                        //all relative PIDLs of files in
                        //the current folder
  
  //Enumerate file objects including hidden ones
  //and excluding folders
  sfFolder->EnumObjects(NULL, SHCONTF_NONFOLDERS    | 
                              SHCONTF_INCLUDEHIDDEN  
                            , &pidList);

  pidList->Reset(); //Make sure we are at the 
                    //start of the enumeration

  //Select all items in the current view
  //using each file PIDL in current folder's enumeration
  while( S_FALSE != (pidList->Next(1, &pidFile, NULL)) )
    pShView->SelectItem(pidFile, SVSI_SELECT);

  //Release the enumeration
  pidList->Release();

  //Move focus to the folder view so that
  //the selected items show properly
  pShView->UIActivate(SVUIA_ACTIVATE_FOCUS);

  //Free shell allocated memory
  pMalloc->Free(pidFile);
  pMalloc->Free(pidFolder);

  pMalloc->Release();
}
/////////////////////////////////////////////////////////////////////////////////

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alex Fotios
Software Developer Freelancer
Brazil Brazil
I have a keen interest in IT Security, Internet applications, and systems/embedded development. My recent research interests have included secure networks, models of trust, trusted agents, information exchange, and software development methodologies.
 
http//linkedin.com/in/fotios
https://www.elance.com/s/fotios/
Follow on   Twitter

Comments and Discussions

 
QuestionProblem with XP SP3 Pinmemberhojjat_rele18-Jul-08 23:37 
QuestionAdding the button Globally? PinmemberLupinTaiwan5-Dec-06 2:30 
GeneralOnFileNameOK() warning - return value opposite!!! PinmemberBaliDawg3-Aug-06 10:08 
QuestionHow to catch the focus Pinmembertaphan17-Oct-05 22:42 
AnswerHow to add the Button to the CFileDialog Pinmembermanyan20284-Aug-14 21:00 
GeneralRe: How to add the Button to the CFileDialog PinmemberAlex Fotios5-Aug-14 5:06 
GeneralRe: How to add the Button to the CFileDialog Pinmembermanyan20285-Aug-14 20:57 
GeneralMaking this work for .NET (edt1 does not exist) Pinmemberjjorsett20-Aug-05 9:38 
GeneralStarting the view as THUMBNAILS PinmemberAlex Evans14-Nov-04 16:47 
GeneralSample source code pls Pinmemberclayton0421-Dec-03 19:49 
GeneralRe: Sample source code pls Pinmembervenka23-Apr-04 9:56 
GeneralRe: Sample source code pls PinmemberLupinTaiwan9-Feb-06 20:57 
QuestionHow to Import the common dialog template as a dialog resource ? PinmemberJean-Marc Molina12-Apr-03 2:54 
GeneralBrowseObject() does not work PinmemberBrunhilde9-Apr-03 0:28 
GeneralRe: BrowseObject() does not work PinmemberMark Webb21-Oct-03 8:14 
GeneralHelp with Tab Order PinmemberColleen3-Jan-03 4:03 
GeneralEasier way to access the list ctrl PinmemberAnonymous27-Feb-02 14:41 
GeneralRe: Easier way to access the list ctrl PinmemberYongQing23-May-02 16:52 
GeneralRe: Easier way to access the list ctrl PinmemberMarte197618-May-04 0:50 
GeneralRe: Easier way to access the list ctrl PinmemberAlexander Yadykin6-May-08 20:59 
GeneralCannot find example code PinmemberGeorge Anescu3-Jan-02 4:32 
GeneralRe: Cannot find example code PinmemberHenri Manson20-Jan-03 11:42 
GeneralRe: Cannot find example code PinmemberLupinTaiwan9-Feb-06 20:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411022.1 | Last Updated 2 Jan 2002
Article Copyright 2002 by Alex Fotios
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid