79

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

在此处输入图像描述

相同的对话框显示在不同计算机上可见的选定文件夹

在此处输入图像描述

我在两台都装有 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 ();
            }

        }
4

14 回答 14

89

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

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

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

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

  • 第三个问题是直到Tree View控件真正作为一个真实的窗口存在时我们才能发送TVM_ENSUREVISIBLE消息,并且直到我们调用该ShowDialog方法时它才存在。然而,这个方法被阻塞了,所以一旦这个方法被调用,我们就没有机会发布我们的消息了。

为了解决这些问题,我创建了一个带有单个方法的静态帮助器类,该方法可用于显示 a FolderBrowserDialog,并使其滚动到所选文件夹。我通过Timer在调用对话的方法之前开始一个简短的处理来管理它ShowDialog,然后在处理程序中跟踪TreeView控件的Timer句柄(即,在显示对话之后)并发送我们的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;
    }
}
于 2013-03-15T19:46:30.557 回答
11

我知道这个线程已经很老了,但是通过扩展方法,可以将它添加到 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);
    }
}
于 2016-09-10T04:07:21.227 回答
9

我使用了来自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

于 2015-04-17T06:25:41.463 回答
8

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

SendKeys.Send ("{TAB}{TAB}{RIGHT}")
于 2016-08-08T18:21:39.970 回答
4

这对我有用

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

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

于 2014-08-28T10:47:59.333 回答
3

我发现:

  1. 如果.SelectedPath以“\”结尾,对话框将向下滚动以使路径可见。
  2. 如果.SelectedPath不以“\”结尾,则路径仍被选中,但不确保可见。
于 2015-01-12T09:44:13.927 回答
3

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

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

对 Hans Passant 的回答:我尝试了这个 Dialog Extension,它有 TextBox,但没有运气。

自定义浏览文件夹对话框以显示路径

于 2011-08-05T06:25:31.890 回答
2

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

我的想法是在显示 folderBrowserDialog.

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

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#。

于 2015-07-11T19:50:30.033 回答
2

我在 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;
}
于 2016-01-29T16:54:58.123 回答
1

我已阅读上述讨论和解决方案。特别是 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);
} 

非常感谢加里·比尼

于 2018-01-18T16:56:45.777 回答
0

这个链接有一个简单的答案,对我来说很好(我有 windows 8.1

FolderBrowserDialog:展开选定的目录

于 2014-03-18T00:08:44.813 回答
0

dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

不一样

dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

SpecialFolder.Desktop 和 SpecialFolder.DesktopDirectory 有什么区别?

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

我发现当任何一个被分配给打开文件夹对话框的 RootFolder 时,结果行为可能会有所不同。

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

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

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

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

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

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

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

解决方法是在 win8 中选择备用 GUI。也许类似的事情可以在win10 prerelease 中完成。

于 2015-06-27T18:31:39.587 回答
0

回应 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

于 2018-03-13T02:33:51.390 回答
-2

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

如果您知道如何在油漆中进行渲染,那么您无能为力。快速油漆好,那是另一回事。

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

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

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

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

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

于 2018-06-23T00:41:04.033 回答