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


76

如该屏幕快照所示,所选文件夹不在视图中。需要向下滚动以查看选定的文件夹。

在此处输入图片说明

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

在此处输入图片说明

我在两台都装有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支持联系。
汉斯·帕桑

Answers:


85

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

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

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

  • 其次,.NETFolderBrowserDialog类的设计者决定对此类进行密封。这个不幸的决定使我们无法从中派生并覆盖窗口消息处理程序。如果我们能够做到这一点,那么TVM_ENSUREVISIBLE当我们WM_SHOWWINDOW在消息处理程序中收到消息时,我们可能试图过帐该消息。

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

为了解决这些问题,我创建了一个静态帮助器类,该类具有一个可用于显示的方法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;
    }
}

6
这应该被标记为答案。我刚刚遇到了同样的问题,并且此代码运行完美。这也是一个非常详细且写得很好的解释。

该方法的第二个参数应该是什么ShowFolderBrowser?该IWin32Window...?
Syspect 2013年

@Syspect-IWin32Window参数只是从其启动文件夹选择器的父窗体。如果您直接从表单代码中调用此方法,则只需使用“ this”关键字作为参数。(从技术上讲,IWin32Window实际上是表单后面hWnd的包装,但是C#向您隐藏了所有与之相关的丑陋东西!)
Brad Oestreicher 2013年

2
在Win7上,我观察到发生了滚动,然后在最初显示对话框后,将系统文件夹(如Libraries等)添加到树中,然后将其重置。设置1000ms的初始间隔足以克服这一问题,尽管它只是顶部的另一张卡!
乔纳森·米切尔

1
在Win10上,就像@Jonathan Mitchell指出的那样,存在计时问题。设置t.Interval = 100; 足以在我的机器上解决此问题(对于速度较慢的机器来说更长吗?)。
avenmore

10

我知道这个线程已经很老了,但是有了扩展方法,可以将其添加到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);
    }
}

1
在x64 Windows 8操作系统上,这对我最有用。但是,我通过在500毫秒后在Timer_Tick事件中添加执行sendkey来扩展它,因为它移到了所选文件夹,然后又恢复到该文件夹​​的根驱动器。因此需要延迟。
hynsey

8

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


1
为我工作。学习一下tab-tab-rightarrow键盘序列滚动到所选目录的乐趣。在C#中:SendKeys.Send("{TAB}{TAB}{RIGHT}");
罗兰

1
“这个失败”:我想“这个”指的是SendKeys的把戏,而“失败”应该是“功能”。
罗兰

7

在VB.Net代码上,只需在显示对话框之前将这一行代码放到右边即可。

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

这个答案真棒!暂时解决了我的问题..真讨厌不得不向下滚动到所选内容..:+1 :. 如果要由我决定,将是我接受的答案:P
Dan Bradbury

3

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

但是,如果RootFolder更改为桌面以外的其他名称,则将无法导航到UNC路径。

回答汉斯·帕桑(Hans Passant):我尝试了这个对话框扩展,它具有TextBox,但是没有运气。

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


3

这对我有用

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

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


3

我发现:

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

抱歉:此解决方案只能工作一半。好像里面有些比赛条件。注意:目录应该存在。
Aleksandr

2

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

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

我自己找到了这个,但是我受到了Brad post的启发。这是我的代码:

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


这个问题已经很老了,所以我不太希望有人尽快做出回应。感谢您的输入!
ZygD 2015年

1
这应该问自己的问题。它不太可能在这里看到。
Stuart Siegler

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

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

非常感谢Gary Beene


0

dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

与...不同

dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

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

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

我发现将其中一个分配给打开文件夹对话框的RootFolder时,所得行为可能有所不同。

作为.RootFolder分配,某些版本的Windows(例如win7)会将其中一个视为“桌面”。也就是说,您可以看到“计算机”子条目,然后打开该条目以查看各个驱动器号。.SelectedPath可以通过任何一种方式选择,但是仅当将桌面的逻辑路径分配给.RootFolder时,所选路径才可见。

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

我认为OP所面临的问题是,当他们应该使用逻辑桌面时,他们将物理桌面用作根。

对于OP的两台不同机器为何做出不同响应,我没有任何解释。我推测他们安装了两个不同版本的.NET框架。

win10预发行版的“浏览文件夹”对话框中存在“卡在桌面上”的问题,这可能是由于win10预发行版附带了更新的.NET框架。不幸的是,我仍然不了解此案(win10)中的所有事实,因为我尚未更新。

PS我发现win8也遇到“卡在桌面上”的症状:

/superuser/869928/windows-8-1-folder-selection-dialog-missing-my-computer-and-sub-items

解决方法是在win8中选择备用GUI。也许可以在win10预发行版中完成类似的操作。


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



-2

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

如果您知道如何在涂料中进行渲染,那么您将不能做太多事情。

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

由于这是一个旧线程,因此发布样本可能永远不会被读取。如果有要求的话,自发布以来,将会有更多的收获。

但是对于希望解决此类问题(例如,将树滚动到“ expected”目录)的人,这里有一些可靠的建议。如果没有立即解决方案的控件或库存在问题,请创建您自己的版本,并尽可能扩展原始版本并修补问题。我已经将所有内容从Windows.Form.Control类升级到Win32库,其唯一目的是获得可预测的准确结果。

好消息是,使用C#,可以使用许多低级控制来实现几乎任何合理的目标,并且C也是。

过去,我花了太多时间来寻找问题的解决方案,而如果我只是重新创建了无法解决的问题,那么可以节省很多时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.