簡體   English   中英

為什么 FolderBrowserDialog 對話框不滾動到選定的文件夾?

[英]Why FolderBrowserDialog dialog does not scroll to selected folder?

如此屏幕截圖所示,所選文件夾不在視圖中。 需要向下滾動才能查看所選文件夾。

在此處輸入圖像描述

相同的對話框顯示在不同計算機上可見的選定文件夾

在此處輸入圖像描述

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

        }

根本問題是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;
    }
}

我知道這個線程已經很老了,但是通過擴展方法,可以將它添加到 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);
    }
}

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

在 VB.Net 代碼上,只需將這行代碼放在顯示對話框之前。

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

這對我有用

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

但只有在第二次使用對話框之后

我發現:

  1. 如果.SelectedPath以“\”結尾,對話框將向下滾動以使路徑可見。
  2. 如果.SelectedPath不以“\”結尾,則該路徑仍被選中,但不確保可見。

我在不同的論壇上讀到,這可能是由於 RootFolder 造成的,因為 SelectedPath 和 RootFolder 是互斥的,這意味着兩者不能共存,但使用默認的 RootFolder(.Desktop),它至少允許爬樹(導航驅動器/文件夾)。

但是,如果將 RootFolder 更改為 Desktop 以外的文件夾,您將無法導航到 UNC 路徑。

對 Hans Passant 的回答:我嘗試了這個 Dialog Extension,它有 TextBox,但沒有運氣。

自定義瀏覽文件夾對話框以顯示路徑

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

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

我已閱讀上述討論和解決方案。 特別是 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);
} 

非常感謝加里·比尼

這個鏈接有一個簡單的答案,對我來說很好(我有 windows 8.1

FolderBrowserDialog:展開選定的目錄

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 中完成。

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

我嘗試了代碼SendKeys.Send("{TAB}{TAB}{RIGHT}"); ,但有時相同的代碼不起作用。

最后,我使用的最佳解決方案是:

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

最好的方法,至少最可靠的方法是制作自己的瀏覽器類對話框。 樹滾動問題多年來一直是個痛點——它永遠不會得到解決!

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

我首先要看的是 GitHub 上的開源 .Net 源代碼,在您選擇的 .Net 版本中,用於您有興趣改進的對話框類。 您可能會驚訝於您只需付出一點努力就可以實現並堅持到底。 只需復制控件並調試到錯誤發生的位置並進行修補 - 這就是 Microsoft 所做的,您也可以!

由於這是一個舊線程並且發布示例可能永遠不會被閱讀。 如果被問到,它會做得更多。

然而,對於希望解決諸如樹滾動到“預期”目錄這樣的問題的人來說,這里有一些可靠的建議。 如果控件或庫存在無法立即解決的問題,請創建您自己的版本,並在可能的情況下擴展原始版本並修補問題。 我已經修改了從 Windows.Form.Control 類到 Win32 庫的所有內容,其唯一目的是獲得可預測和准確的結果。

好消息是,對於 C#,有很多低級控制可用於實現幾乎任何合理的目標,C 也是如此。

在過去,我花了太多時間來尋找一個問題的解決方案,如果我剛剛重新創建了不起作用的東西,我會節省很多時間。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM