为什么FolderBrowserDialog对话框不会滚动到所选文件夹?

Mun*_*war 70 c c# folderbrowserdialog

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

在此输入图像描述

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

在此输入图像描述

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

        }
Run Code Online (Sandbox Code Playgroud)

Bra*_*her 81

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

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

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

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

  • 第三个问题是我们不能发送TVM_ENSUREVISIBLE消息,直到Tree View控件实际存在为真实窗口,并且在我们调用该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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该标记为答案.我刚遇到同样的问题,这段代码完美无缺.这也是一个非常详细和写得很好的解释. (6认同)
  • 在 Win7 上,我观察到滚动发生,然后在最初显示对话框后将系统文件夹(例如库等)添加到树中时被重置。将初始间隔设置为 1000 毫秒就足以克服这个问题,尽管它只是在顶部多一张卡! (2认同)

小智 8

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

SendKeys.Send ("{TAB}{TAB}{RIGHT}")
Run Code Online (Sandbox Code Playgroud)


Bri*_*ead 8

我知道这个线程已经过时了,但是使用扩展方法,可以将其添加到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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Tob*_*uss 7

我使用了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 ();
Run Code Online (Sandbox Code Playgroud)

这不是最好的方式,但它对我有用.
没有RootFolder它不适用于第一次通话,但在第二次和以下.有了它,它始终有效.

正如其他人观察到的那样,这种失败取决于操作系统:
我使用的是Win 7 Pro x64 SP1


小智 5

这对我有用

folderBrowserDialog1.Reset();  
folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
folderBrowserDialog1.SelectedPath = WorkingFolder;
Run Code Online (Sandbox Code Playgroud)

但仅在第二次使用该对话框之后


Mun*_*war 3

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

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

对 Hans Passant 的回答:我尝试了这个对话框扩展,它有文本框,但没有运气。

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