Why FolderBrowserDialog dialog does not scroll to selected folder?

Viewed 49703

As show in this screen shot, the selected folder is not in the view. It needs to be scrolled down to view the selected folder.

enter image description here

Same dialog shows selected folder visible on different computer

enter image description here

I ran it on two computers both having windows 7. It works correctly on one but does not on 2nd. It looks something with windows environment instead some code issue? Can anyone suggest any fix?

There is no change in code. I used longer paths from different drives but results are same.

private void TestDialog_Click ( object sender, EventArgs e )
        {
            //Last path store the selected path, to show the same directory as selected on next application launch.
            //Properties.Settings.Default.LastPath

            FolderBrowserDialog dlgFolder = new FolderBrowserDialog ();

            dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

            dlgFolder.SelectedPath = Properties.Settings.Default.LastPath;

            if (dlgFolder.ShowDialog () == System.Windows.Forms.DialogResult.OK)
            {

                Properties.Settings.Default.LastPath = dlgFolder.SelectedPath;               

                Properties.Settings.Default.Save ();
            }

        }
15 Answers

I have read the above discussion and solutions. Particularly Brat Oestreicher put me in the right direction. In essence, we must first find the TreeView control in the SHBrowseForFolder dialog, and send that window the TVM_ENSUREVISIBLE message. The following does this in C.

#include <windows.h>
#include <objbase.h>
#include <objidl.h>
#include <Shlobj.h>
#include <Dsclient.h>
#include <wchar.h>
// 
//  EnumCallback - Callback function for EnumWindows 
// 
static BOOL CALLBACK EnumCallback(HWND hWndChild, LPARAM lParam)
{
   char szClass[MAX_PATH];
   HTREEITEM hNode;
   if (GetClassName(hWndChild, szClass, sizeof(szClass))
   &&  strcmp(szClass,"SysTreeView32")==0) {
      hNode = TreeView_GetSelection(hWndChild);    // found the tree view window
      TreeView_EnsureVisible (hWndChild, hNode);   // ensure its selection is visible
      return(FALSE);   // done; stop enumerating
   }
   return(TRUE);       // continue enumerating
}
// 
//  BrowseCallbackProc - Callback function for SHBrowseForFolder 
// 
static INT CALLBACK BrowseCallbackProc (HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData) 
{
    switch (uMsg) 
    { 
        case BFFM_INITIALIZED:
            SendMessage (hWnd, BFFM_SETEXPANDED, TRUE, lpData);    // expand the tree view
            SendMessage (hWnd, BFFM_SETSELECTION, TRUE, lpData);   // select the item
            break;
        case BFFM_SELCHANGED:
            EnumChildWindows(hWnd, EnumCallback,0);
            break;
    } 
    return 0; 
} 
// 
//  SelectDirectory - User callable entry point 
// 
int SelectDirectory (HWND hWndParent, char *path, int pathSize) 
{ 
    BROWSEINFO bi = {0};
    LPITEMIDLIST pidl = NULL;
    wchar_t ws[MAX_PATH];

    CoInitialize(0);
    if (pathSize < MAX_PATH) return(FALSE);

    swprintf(ws, MAX_PATH, L"%hs", path);

    bi.hwndOwner = hWndParent; 
    bi.lpszTitle = "Select Directory"; 
    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
    bi.lpfn = BrowseCallbackProc;
    bi.lParam = (LPARAM) ws;

    pidl = SHBrowseForFolder (&bi); 
    if (pidl != NULL) 
    { 
        LPMALLOC pMalloc = NULL; 
        SHGetPathFromIDList (pidl, path);
        path[pathSize-1]= '\0';

        SHGetMalloc(&pMalloc);
        pMalloc->lpVtbl->Free(pMalloc,pidl);    // deallocate item 
        pMalloc->lpVtbl->Release(pMalloc);

        return (TRUE);
    } 
    return (FALSE);
} 

Many thanks to Gary Beene.

I tried the code SendKeys.Send("{TAB}{TAB}{RIGHT}");, but the same code sometimes just didn't work.

At the end the best solution which I went with, where I can't get it not working, was:

public static class FolderBrowserDialogExt
{
    public static void ScrollSelectedPathIntoView(this FolderBrowserDialog fbd)
    {
        System.Threading.Tasks.Task.Run(() =>
        {
            SendKeys.SendWait("{TAB}");
            SendKeys.SendWait("{TAB}");
            SendKeys.SendWait("{RIGHT}");
        });
    }
}

void SomeMeth(string selPath)
{
    using (var fbd = new FolderBrowserDialog())
    {
        fbd.SelectedPath = selPath;
        fbd.ScrollSelectedPathIntoView();
        fbd.ShowDialog();//fbd.ShowDialog(owner);
    }
}

The best approach, at least the most reliable is to make your own browser class dialog box. The tree scrolling issue has been a pain for many years - it will never get fixed!

If you know how to render in paint there is not much you can't do.. fast in paint well that is another story.

The first place I would look is at the open source .Net source code on GitHub, in your .Net version of choice, for the dialog class you're interested in improving. You may be surprised what you can achieve with a little effort and follow through. Just duplicate the control and debug to the point where the error occurs and patch - that'a what Microsoft does, so too can you!

Since this is an old thread and posting samples may never get read. It would make more since to post if asked.

Yet for someone looking to solve such an issue as with tree scrolling to the "expected" directory, here is some solid advise. If an issue exists with a control or library that has no immediate solution, create your own version, when possible extend the original and patch the problem. I've revamped everything from the Windows.Form.Control class to Win32 libraries for the sole purpose of getting predictable and accurate results.

The good news is that with C# there is a lot of low level control available to achieve almost any reasonable objective and the is C too.

In the past I have spent way too many hours searching for a solution to a problem where had I just recreated what was not working a lot of time would have been saved.

Related