【问题标题】:Why FolderBrowserDialog dialog does not scroll to selected folder?为什么 FolderBrowserDialog 对话框不滚动到选定的文件夹?
【发布时间】:2011-10-20 00:09:33
【问题描述】:

如此屏幕截图所示,所选文件夹不在视图中。需要向下滚动才能查看所选文件夹。

同一对话框显示所选文件夹在不同计算机上可见

我在两台装有 Windows 7 的计算机上运行它。它在一台计算机上正常工作,但在第二台计算机上却不能。它看起来与 Windows 环境有关,而不是一些代码问题?任何人都可以提出任何解决方法吗?

代码没有变化。我使用了来自不同驱动器的较长路径,但结果相同。

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 ();
            }

        }

【问题讨论】:

  • 是的,它是环保的。该对话框是在 Windows 中实现的,而不是在 Silverlight 中。很可能是 Windows 错误,我敢打赌通常不存在的“文件夹”文本框是根本原因。没有它,“问题”文件夹将可见。如果您想这样做,请联系 Microsoft 支持。

标签: c# c folderbrowserdialog


【解决方案1】:

最好的方法,至少最可靠的方法是制作你自己的浏览器类对话框。多年来,树滚动问题一直是个痛点 - 它永远不会得到解决!

如果您知道如何在绘画中进行渲染,那么您可以做的事情并不多。快速绘画好,那是另一回事。

我首先要看的是 GitHub 上的开源 .Net 源代码,在您选择的 .Net 版本中,用于您有兴趣改进的对话框类。您可能会惊讶于您通过一点点努力就能实现的目标并坚持到底。只需复制控件并调试到错误发生的位置并打补丁——这就是微软所做的,你也可以!

由于这是一个旧线程并且发布示例可能永远不会被阅读。如果被问到,发布后会做得更多。

然而,对于希望解决诸如树滚动到“预期”目录这样的问题的人来说,这里有一些可靠的建议。如果控件或库存在无法立即解决的问题,请创建您自己的版本,并在可能的情况下扩展原始版本并修补问题。我已经修改了从 Windows.Form.Control 类到 Win32 库的所有内容,其唯一目的是获得可预测和准确的结果。

好消息是,对于 C#,有很多低级控制可用于实现几乎任何合理的目标,C 也是如此。

在过去,我花费了太多时间来寻找解决问题的方法,如果我刚刚重新创建了不起作用的东西,我会节省很多时间。

【讨论】:

    【解决方案2】:

    这个链接有一个简单的答案,对我很有效(我有 Windows 8.1

    FolderBrowserDialog: Expanding the selected directory

    【讨论】:

    • 这会展开选定的文件夹,但会错误选择第一个子文件夹,并且不能解决滚动问题。
    • 你为什么链接到另一个网站而不至少复制这里的解决方案?
    【解决方案3】:

    响应 Marc F 的帖子 - 我已将 VB.Net 转换为 C#

        public enum GW
        {
            HWNDFIRST = 0,
            HWNDLAST = 1,
            HWNDNEXT = 2,
            HWNDPREV = 3,
            OWNER = 4,
            CHILD = 5,
            ENABLEDPOPUP = 6
        }
    
        [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SendMessageW", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr SendMessageW(IntPtr hWnd, uint msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
        [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindowExW", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndChildAfter, [MarshalAs(UnmanagedType.LPWStr)] string lpszClass, [MarshalAs(UnmanagedType.LPWStr)] string lpszWindow);
        [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetWindow", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        public static extern UInt32 GetWindow(IntPtr hwnd, UInt32 wCmd);
        [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetDesktopWindow", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr GetDesktopWindow();
        [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetClassNameA", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        public static extern int GetClassName(IntPtr hwnd, System.Text.StringBuilder lpClassName, int nMaxCount);
    
        private void FolderBrowserDialog_EnsureVisible(FolderBrowserDialog FB, IntPtr _Owner)
        {
            IntPtr hwnd = System.IntPtr.Zero;
            System.Text.StringBuilder sClassname = new System.Text.StringBuilder(256);
            Thread.Sleep(50); //necessary to let FolderBrowserDialog construct its window
            hwnd = GetDesktopWindow(); //Desktop window handle.
            hwnd = (System.IntPtr)GetWindow(hwnd, (UInt32)GW.CHILD); //We will find all children.
            while (!(hwnd == (System.IntPtr)0))
            {
                if (GetWindow(hwnd, (UInt32)GW.OWNER) == (UInt32)_Owner) //If one window is owned by our main window...
                {
                    GetClassName(hwnd, sClassname, 255);
                    if (sClassname.ToString() == "#32770") //Check if the class is FolderBrowserDialog.
                    {
                        break; //Then we found it.
                    }
                }
                hwnd = (System.IntPtr)GetWindow(hwnd, (UInt32)GW.HWNDNEXT); //Next window.
            } //If no found then exit.
            if (hwnd == (System.IntPtr)0)
            {
                return;
            }
            IntPtr hChild = (System.IntPtr)0;
            IntPtr hTreeView = (System.IntPtr)0;
            int i = 0;
            do
            {
                i += 1;
                if (i > 1000) //Security to avoid infinite loop.
                {
                    return;
                }
                hChild = FindWindowExW(hwnd, hChild, null, null); //Look for children windows of FolderBrowserDialog.
                hTreeView = FindWindowExW(hChild, (System.IntPtr)0, "SysTreeView32", null); //Look for treeview of FolderBrowserDialog.
                Thread.Sleep(5); //delay necessary because FolderBrowserDialog is in construction, then treeview maybe not yet exist.
            } while (hTreeView == (System.IntPtr)0);
            if (SendMessageW(hwnd, 0x46A, 1, FB.SelectedPath) == (System.IntPtr)0) //Send message BFFM_SETEXPANDED to FolderBrowserDialog.
            {
                SendMessageW(hTreeView, 0x7, 0, null); //Send message WM_SETFOCUS to the treeeview.
            }
        }
    

    对此进行了测试,它工作正常。确保引用 System.Runtime.InteropServices、System.Threading、System.Threading.Tasks

    【讨论】:

      【解决方案4】:

      我已阅读上述讨论和解决方案。特别是 Brat Oestreicher 让我朝着正确的方向前进。本质上,我们必须首先在SHBrowseForFolder 对话框中找到TreeView 控件,并向该窗口发送TVM_ENSUREVISIBLE 消息。以下在 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);
      } 
      

      非常感谢Gary Beene

      【讨论】:

        【解决方案5】:

        根本问题是FolderBrowserDialog 中的设计决策不佳。首先,我们需要认识到FolderBrowserDialog 不是.NET 控件,而是Common Dialog,并且是Windows 的一部分。此对话框的设计者选择在显示对话框并选择初始文件夹后不向 TreeView 控件发送TVM_ENSUREVISIBLE 消息。此消息会导致 TreeView 控件滚动,以便当前选定的项目在窗口中可见。

        因此,我们需要做的就是将作为FolderBrowserDialog 一部分的TreeView 发送到TVM_ENSUREVISIBLE 消息,一切都会很好。正确的?嗯,没那么快。这确实是答案,但有些事情阻碍了我们。

        • 首先,因为FolderBrowserDialog 不是真正的.NET 控件,它没有内部Controls 集合。这意味着我们不能只从 .NET 中查找和访问 TreeView 子控件。

        • 其次,.NET FolderBrowserDialog 类的设计者决定密封这个类。这个不幸的决定阻止我们从它派生并覆盖窗口消息处理程序。如果我们能够做到这一点,我们可能会在消息处理程序中收到WM_SHOWWINDOW 消息时尝试发布TVM_ENSUREVISIBLE 消息。

        • 第三个问题是,直到 Tree View 控件真正作为真实窗口存在时,我们才能发送 TVM_ENSUREVISIBLE 消息,并且在调用 ShowDialog 方法之前它不存在。但是,此方法会阻塞,因此一旦调用此方法,我们将没有机会发布消息。

        为了解决这些问题,我用一个方法创建了一个静态帮助器类,该方法可用于显示FolderBrowserDialog,并使其滚动到选定的文件夹。我通过在调用对话的ShowDialog 方法之前启动一个简短的Timer 来管理它,然后在Timer 处理程序中跟踪TreeView 控件的句柄(即,在显示对话之后)并发送我们的TVM_ENSUREVISIBLE 消息。

        这个解决方案并不完美,因为它依赖于一些关于FolderBrowserDialog 的先验知识。具体来说,我使用它的窗口标题找到了对话。这将与非英语安装中断。我使用对话项 ID 而不是标题文本或类名来跟踪对话中的子控件,因为我觉得随着时间的推移这会更加可靠。

        此代码已在 Windows 7(64 位)和 Windows XP 上测试。

        代码如下: (您可能需要:using System.Runtime.InteropServices;

        public static class FolderBrowserLauncher
        {
            /// <summary>
            /// Using title text to look for the top level dialog window is fragile.
            /// In particular, this will fail in non-English applications.
            /// </summary>
            const string _topLevelSearchString = "Browse For Folder";
        
            /// <summary>
            /// These should be more robust.  We find the correct child controls in the dialog
            /// by using the GetDlgItem method, rather than the FindWindow(Ex) method,
            /// because the dialog item IDs should be constant.
            /// </summary>
            const int _dlgItemBrowseControl = 0;
            const int _dlgItemTreeView = 100;
        
            [DllImport("user32.dll", SetLastError = true)]
            static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        
            [DllImport("user32.dll")]
            static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
        
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
        
            /// <summary>
            /// Some of the messages that the Tree View control will respond to
            /// </summary>
            private const int TV_FIRST = 0x1100;
            private const int TVM_SELECTITEM = (TV_FIRST + 11);
            private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
            private const int TVM_GETITEM = (TV_FIRST + 12);
            private const int TVM_ENSUREVISIBLE = (TV_FIRST + 20);
        
            /// <summary>
            /// Constants used to identity specific items in the Tree View control
            /// </summary>
            private const int TVGN_ROOT = 0x0;
            private const int TVGN_NEXT = 0x1;
            private const int TVGN_CHILD = 0x4;
            private const int TVGN_FIRSTVISIBLE = 0x5;
            private const int TVGN_NEXTVISIBLE = 0x6;
            private const int TVGN_CARET = 0x9;
        
        
            /// <summary>
            /// Calling this method is identical to calling the ShowDialog method of the provided
            /// FolderBrowserDialog, except that an attempt will be made to scroll the Tree View
            /// to make the currently selected folder visible in the dialog window.
            /// </summary>
            /// <param name="dlg"></param>
            /// <param name="parent"></param>
            /// <returns></returns>
            public static DialogResult ShowFolderBrowser( FolderBrowserDialog dlg, IWin32Window parent = null )
            {
                DialogResult result = DialogResult.Cancel;
                int retries = 10;
        
                using (Timer t = new Timer())
                {
                    t.Tick += (s, a) =>
                    {
                        if (retries > 0)
                        {
                            --retries;
                            IntPtr hwndDlg = FindWindow((string)null, _topLevelSearchString);
                            if (hwndDlg != IntPtr.Zero)
                            {
                                IntPtr hwndFolderCtrl = GetDlgItem(hwndDlg, _dlgItemBrowseControl);
                                if (hwndFolderCtrl != IntPtr.Zero)
                                {
                                    IntPtr hwndTV = GetDlgItem(hwndFolderCtrl, _dlgItemTreeView);
        
                                    if (hwndTV != IntPtr.Zero)
                                    {
                                        IntPtr item = SendMessage(hwndTV, (uint)TVM_GETNEXTITEM, new IntPtr(TVGN_CARET), IntPtr.Zero);
                                        if (item != IntPtr.Zero)
                                        {
                                            SendMessage(hwndTV, TVM_ENSUREVISIBLE, IntPtr.Zero, item);
                                            retries = 0;
                                            t.Stop();
                                        }
                                    }
                                }
                            }
                        }
        
                        else
                        {
                            //
                            //  We failed to find the Tree View control.
                            //
                            //  As a fall back (and this is an UberUgly hack), we will send
                            //  some fake keystrokes to the application in an attempt to force
                            //  the Tree View to scroll to the selected item.
                            //
                            t.Stop();
                            SendKeys.Send("{TAB}{TAB}{DOWN}{DOWN}{UP}{UP}");
                        }
                    };
        
                    t.Interval = 10;
                    t.Start();
        
                    result = dlg.ShowDialog( parent );
                }
        
                return result;
            }
        }
        

        【讨论】:

        • 这应该被标记为答案。我刚刚遇到了同样的问题,这段代码运行得很好。这也是一个非常详细且写得很好的解释。
        • ShowFolderBrowser 方法的第二个参数应该是什么? IWin32Window... ?
        • @Syspect - IWin32Window 参数只是启动文件夹选择器的父窗体。如果您直接从您的表单代码中调用它,您可以只使用“this”关键字作为参数。 (从技术上讲,IWin32Window 实际上是窗体后面的 hWnd 的包装器,但是 C# 隐藏了所有与它有关的丑陋的东西!)
        • 在 Win7 上,我观察到滚动发生然后被重置,因为在最初显示对话框后,系统文件夹(如库等)被添加到树中。将初始间隔设置为 1000 毫秒就足以克服这个问题,尽管它只是顶部多一张卡!
        • 在 Win10 上,就像@Jonathan Mitchell 指出的那样,存在时间问题。设置 t.Interval = 100;足以在我的机器上解决这个问题(较慢的机器需要更长的时间?)。
        【解决方案6】:

        我知道这个线程已经很老了,但是通过扩展方法,可以将它添加到 FolderBrowserDialog.ShowDialog 方法中,然后在需要的地方重复使用。

        示例(如下)只是使用简单的 SendKeys 方法(我讨厌这样做,但在这种情况下,它运行良好)。当使用 SendKeys 方法跳转到对话框中的选定文件夹时,如果您在 Visual Studio 中对此进行调试,则 SendKeys 调用适用于当前窗口,这将是活动的 VS 窗口。为了更加万无一失并避免错误的窗口获取 SendKeys 消息,扩展方法将包含将消息发送到特定窗口的外部方法调用,类似于 Marc F 发布的内容,但翻译为 C#。

        internal static class FolderBrowserDialogExtension
        {
            public static DialogResult ShowDialog(this FolderBrowserDialog dialog, bool scrollIntoView)
            {
                return ShowDialog(dialog, null, scrollIntoView);
            }
        
            public static DialogResult ShowDialog(this FolderBrowserDialog dialog, IWin32Window owner, bool scrollIntoView)
            {
                if (scrollIntoView)
                {
                    SendKeys.Send("{TAB}{TAB}{RIGHT}");
                }
        
                return dialog.ShowDialog(owner);
            }
        }
        

        【讨论】:

        • 这对我在 x64 Windows 8 操作系统上最有帮助。但是,我通过在 500 毫秒后在 Timer_Tick 事件中添加执行 sendkeys 来扩展它,因为它移动到选定的文件夹,然后恢复到该文件夹​​的根驱动器。所以需要延迟。
        【解决方案7】:

        在 VB.Net 代码中,只需将这行代码放在显示对话框之前。

        SendKeys.Send ("{TAB}{TAB}{RIGHT}")
        

        【讨论】:

        • 这个答案太棒了!暂时解决了我的问题..真的很讨厌必须向下滚动到选择.. :+1:.如果由我决定,这将是我接受的答案:P
        • 很遗憾,这在我的系统(Windows 10)上不起作用;
        • @Phil,如果您显示“新建文件夹”按钮 SendKeys.Send ("{RIGHT}") 为我工作。
        【解决方案8】:

        我在 c++ /mfc 中遇到了同样的问题。我在 BFFM_INITIALIZED 回调中使用 ::PostMessage 而不是 ::SendMessage 来放置 TVM_ENSUREVISIBLE 消息对我有用

            case BFFM_INITIALIZED: 
        {
        // select something
        ::SendMessage(m_hDialogBox, BFFM_SETSELECTION, TRUE, (LPARAM) pszSelection);
        
        
        // find tree control
        m_hTreeCtrl = 0;
        HWND hchild = GetWindow(hWnd, GW_CHILD) ;
        while (hchild != NULL)
        {
          VS_TChar classname[200] ;
          GetClassName(hchild, classname, 200) ;
        
          if (VS_strcmp(classname, _T("SHBrowseForFolder ShellNameSpace Control")) == 0)
          {
            HWND hlistctrl = GetWindow(hchild, GW_CHILD) ;
            do
            { 
              GetClassName(hlistctrl, classname, 200) ;
              if (lstrcmp(classname, _T("SysTreeView32")) == 0)
              {
                m_hTreeCtrl = hlistctrl;
                break ;   
              }
        
              hlistctrl = GetWindow(hlistctrl, GW_HWNDNEXT) ;
            } while (hlistctrl != NULL);
          }      
          if (m_hTreeCtrl)
            break;
          hchild = GetWindow(hchild, GW_HWNDNEXT);      
        }
        
        if (m_hTreeCtrl)
        {
          int item = ::SendMessage(m_hTreeCtrl, TVM_GETNEXTITEM, TVGN_CARET, 0);
          if (item != 0)             
            ::PostMessage(m_hTreeCtrl, TVM_ENSUREVISIBLE,0,item);
        }
        break;
        }
        

        【讨论】:

          【解决方案9】:

          我在 VB.NET 中计算了一些东西,因此很容易将其转换为 C#。 我是法国人,我是 VB 的初学者。 无论如何,你可以试试我的解决方案。

          我的想法是在显示 folderBrowserDialog 之前启动一个异步任务。

          我自己发现了这个,但我受到了 Brad 帖子的启发。 这是我的代码:

          Imports System.Threading.Tasks
          Imports Microsoft.VisualBasic.FileIO.FileSystem
          
          Public Enum GW
              HWNDFIRST = 0
              HWNDLAST = 1
              HWNDNEXT = 2
              HWNDPREV = 3
              OWNER = 4
              CHILD = 5
              ENABLEDPOPUP = 6
          End Enum
          
          Public Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As IntPtr
          Public Declare Function FindWindowExW Lib "user32.dll" (ByVal hWndParent As IntPtr, ByVal hWndChildAfter As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszClass As String, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszWindow As String) As IntPtr
          Public Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Long) As Long
          Public Declare Function GetDesktopWindow Lib "user32" () As IntPtr
          Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
          
          Private Sub FolderBrowserDialog_EnsureVisible(FB As FolderBrowserDialog, _Owner As IntPtr)
              Dim hwnd As IntPtr
              Dim sClassname As New System.Text.StringBuilder(256)
              Thread.Sleep(50)                                     'necessary to let FolderBrowserDialog construct its window
              hwnd = GetDesktopWindow()                            'Desktop window handle.
              hwnd = GetWindow(hwnd, GW.CHILD)                     'We will find all children.
              Do Until hwnd = 0
                  If GetWindow(hwnd, GW.OWNER) = _Owner Then       'If one window is owned by our main window...
                      GetClassName(hwnd, sClassname, 255)
                      If sClassname.ToString = "#32770" Then       'Check if the class is FolderBrowserDialog.
                          Exit Do                                  'Then we found it.
                      End If
                  End If
                  hwnd = GetWindow(hwnd, GW.HWNDNEXT)              'Next window.
              Loop                                                 'If no found then exit.
              If hwnd = 0 Then Exit Sub
              Dim hChild As IntPtr = 0
              Dim hTreeView As IntPtr = 0
              Dim i As Integer = 0
              Do
                  i += 1
                  If i > 1000 Then Exit Sub                                       'Security to avoid infinite loop.
                  hChild = FindWindowExW(hwnd, hChild, Nothing, Nothing)          'Look for children windows of FolderBrowserDialog.
                  hTreeView = FindWindowExW(hChild, 0, "SysTreeView32", Nothing)  'Look for treeview of FolderBrowserDialog.
                  Thread.Sleep(5)                                                 'delay necessary because FolderBrowserDialog is in construction, then treeview maybe not yet exist.
              Loop While hTreeView = 0
              If SendMessageW(hwnd, &H46A, 1, FB.SelectedPath) = 0 Then           'Send message BFFM_SETEXPANDED to FolderBrowserDialog.
                  SendMessageW(hTreeView, &H7, 0, Nothing)                        'Send message WM_SETFOCUS to the treeeview.
              End If
          End Sub
          
          
          Dim My_save_dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\My-Saves"
          
          Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
              Dim FolderBrowserDialog1 As New FolderBrowserDialog
              FolderBrowserDialog1.Description = "Choose your save files path."
              If Directory.Exists(My_save_dir) Then
                  FolderBrowserDialog1.SelectedPath = My_save_dir
              Else
                  FolderBrowserDialog1.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
              End If
          
              Dim Me_handle = Me.Handle         'Store the main handle to compare after with each windows owner.
              Task.Run(Sub() FolderBrowserDialog_EnsureVisible(FolderBrowserDialog1, Me_handle))      'Here's the trick, run an asynchronous task to modify the folderdialog.
              If FolderBrowserDialog1.ShowDialog(Me) = System.Windows.Forms.DialogResult.OK Then
                  My_save_dir = FolderBrowserDialog1.SelectedPath
              End If
          End Sub
          

          我正在等待您的建议。 有人可以把它翻译成 C#,因为我不懂 C#。

          【讨论】:

          • 这个问题已经很老了,所以我不希望有人会很快回复。感谢您的意见!
          • 这应该在它自己的问题中提出。不太可能在这里看到。
          【解决方案10】:

          dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

          不一样

          dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

          What's the difference between SpecialFolder.Desktop and SpecialFolder.DesktopDirectory?

          链接的线程表明作为路径,它们确实得到相同的结果。但它们并不相同,因为一个是逻辑路径,另一个是物理路径。

          我发现当其中一个被分配给打开文件夹对话框的根文件夹时,产生的行为可能会有所不同。

          作为 .RootFolder 分配,某些版本的 windows,如 win7,将任一版本视为“桌面”。也就是说,您可以看到“计算机”子条目,然后打开它以查看各个驱动器号。 .SelectedPath 以任一方式被选中,但只有在桌面的逻辑路径 分配给 .RootFolder 时,所选路径才可见。

          更糟糕的是,在win10预发布中使用浏览文件夹对话框时,“DesktopDirectory”似乎只是桌面目录的内容,与逻辑桌面目录没有任何链接。并且没有在其下列出任何子项。如果为 win7 编写的应用程序试图与 win10 一起使用,则非常令人沮丧。

          我认为 OP 遇到的问题是他们使用物理桌面作为根,而他们应该使用逻辑桌面。

          我没有解释为什么 OP 的两台不同机器的响应不同。我推测他们安装了两个不同版本的 .NET 框架。

          win10 预发行版在浏览文件夹对话框中出现“卡在桌面”问题的事实可能是由于 win10 预发行版附带的较新的 .NET 框架。不幸的是,我仍然对这个(win10)案例中的所有事实一无所知,因为我还没有更新。

          附:我发现win8也出现了“卡在桌面”的症状:

          https://superuser.com/questions/869928/windows-8-1-folder-selection-dialog-missing-my-computer-and-sub-items

          解决方法是在 win8 中选择备用 GUI。或许在 win10 预发布版中也可以做类似的事情。

          【讨论】:

            【解决方案11】:

            我使用了来自https://www.daniweb.com/software-development/csharp/threads/300578/folderbrowserdialog-expanding-the-selected-directory-的解决方法

            FolderBrowserDialog^ oFBD = gcnew FolderBrowserDialog;
            oFBD->RootFolder = Environment::SpecialFolder::MyComputer;
            oFBD->SelectedPath = i_sPathImport;
            oFBD->ShowNewFolderButton = false;     // use if appropriate in your application
            SendKeys::Send ("{TAB}{TAB}{RIGHT}");  // <<-- Workaround
            ::DialogResult oResult = oFBD->ShowDialog ();
            

            这不是最好的方法,但对我有用。
            如果没有RootFolder,它在第一次调用时不起作用,但在第二次及以下调用时不起作用。有了它,它总是有效的。

            正如其他人所观察到的,此故障取决于操作系统:
            我正在使用 Win 7 Pro x64 SP1

            【讨论】:

            • 为我工作。有趣的是,tab-tab-rightarrow 键盘序列滚动到选定的目录。在 C# 中:SendKeys.Send("{TAB}{TAB}{RIGHT}");
            • “this failure”:我想“this”指的是SendKeys技巧,“failure”应该是“feature”。
            【解决方案12】:

            我发现:

            1. 如果.SelectedPath 以“\”结尾,对话框将向下滚动以使路径可见。
            2. 如果.SelectedPath 不以“\”结尾,则仍选择该路径,但不确保可见。

            【讨论】:

            • 对不起:这个解决方案只工作了一半。似乎里面有一些比赛条件。注意:目录应该存在。
            • 我还没有看到这个作品。选择始终停留在根目录。
            【解决方案13】:

            这对我有用

            folderBrowserDialog1.Reset();  
            folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
            folderBrowserDialog1.SelectedPath = WorkingFolder;
            

            但只有在第二次使用对话框之后

            【讨论】:

              【解决方案14】:

              我在不同的论坛上读到这可能是由于 RootFolder 造成的,因为 SelectedPath 和 RootFolder 是互斥的,这意味着两者不能共存,但使用默认的 RootFolder(.Desktop),它至少允许爬树(导航驱动器/文件夹)。

              但是,如果将 RootFolder 更改为 Desktop 以外的文件夹,您将无法导航到 UNC 路径。

              对汉斯·帕桑特的回答: 我试过这个对话框扩展,它有文本框,但没有运气。

              Customising the browse for folder dialog to show the path

              【讨论】:

                猜你喜欢
                • 2018-01-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-04-29
                • 2015-11-29
                相关资源
                最近更新 更多