Programing

FolderBrowserDialog 대화 상자가 선택한 폴더로 스크롤되지 않는 이유는 무엇입니까?

lottogame 2020. 11. 2. 07:33
반응형

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

        }

근본적인 문제는 FolderBrowserDialog. 첫째, FolderBrowserDialog이 컨트롤이 .NET 컨트롤이 아니라 Common DialogWindows의 일부 라는 사실을 깨달아야합니다 . 이 대화 상자의 디자이너는 TVM_ENSUREVISIBLE대화 상자가 표시되고 초기 폴더가 선택된 후 TreeView 컨트롤에 메시지 를 보내지 않도록 선택했습니다. 이 메시지는 현재 선택된 항목이 창에 표시되도록 TreeView 컨트롤이 스크롤되도록합니다.

그래서, 우리는이 문제를 해결하기 위해 필요한 모든이의 일부입니다 TreeView에 보낼 수 있습니다 큰 것 메시지 다. 권리? 글쎄, 그렇게 빠르지는 않습니다. 이것이 실제로 답이지만, 우리를 가로막는 몇 가지가 있습니다.FolderBrowserDialogTVM_ENSUREVISIBLE

  • 첫째, FolderBrowserDialog실제로 .NET 컨트롤이 아니기 때문에 내부 Controls컬렉션 이 없습니다 . 즉, .NET에서 TreeView 자식 컨트롤을 찾아서 액세스 할 수 없습니다.

  • 둘째, .NET FolderBrowserDialog클래스 의 디자이너는 이 클래스 봉인 하기로 결정했습니다 . 이 불행한 결정은 우리가 그것에서 파생되고 창 메시지 처리기를 재정의하는 것을 방해합니다. 이 작업을 수행 할 수 있었다면 메시지 처리기에서 TVM_ENSUREVISIBLE메시지를 받았을 때 메시지 를 게시하려고 시도했을 수 있습니다 WM_SHOWWINDOW.

  • 세 번째 문제는 TVM_ENSUREVISIBLETree 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;
    }
}

이 스레드가 오래되었다는 것을 알고 있지만 확장 메서드를 사용하면 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}")

나는 SelectedPath와 RootFolder가 상호 배타적이기 때문에 RootFolder로 인한 것일 수 있다는 것을 다른 포럼에서 읽었습니다. 즉, 둘 다 공존 할 수 없지만 기본 RootFolder (.Desktop)를 사용하면 최소한 트리를 등반 할 수 있습니다 (드라이브 탐색 / folders).

그러나 RootFolder가 Desktop이 아닌 다른 것으로 변경되면 UNC 경로로 이동할 수 없습니다.

Hans Passant에 대한 답변 : TextBox가있는이 Dialog Extension을 시도했지만 운이 없습니다.

경로를 표시하도록 폴더 찾아보기 대화 상자 사용자 정의


나는 다음을 발견했다 :

  1. .SelectedPath"\"로 끝나는 경우 대화 상자가 아래로 스크롤되어 경로가 표시됩니다.
  2. .SelectedPath가 "\"로 끝나지 않으면 경로가 여전히 선택되지만 표시되지는 않습니다.

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 #으로 번역 할 수 있습니다.


C ++ / mfc에서 동일한 문제가 발생합니다. TVM_ENSUREVISIBLE 메시지를 배치하기 위해 BFFM_INITIALIZED 콜백에서 :: SendMessage 대신 :: PostMessage를 사용하는 것이 효과적이었습니다.

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

이것은 나를 위해 작동합니다

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

하지만 대화를 두 번째 사용한 후에 만


위의 토론과 해결책을 읽었습니다. 특히 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 에게 감사드립니다 .


dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

다음과 같지 않다

dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

SpecialFolder.Desktop과 SpecialFolder.DesktopDirectory의 차이점은 무엇입니까?

연결된 스레드는 경로로서 동일한 결과를 얻음을 나타냅니다. 그러나 하나는 논리적 경로이고 다른 하나는 물리적 경로이기 때문에 동일하지 않습니다.

둘 중 하나가 폴더 열기 대화 상자의 RootFolder에 할당되었을 때 결과 동작이 다를 수 있음을 발견했습니다.

.RootFolder 할당으로 win7과 같은 일부 버전의 창은 둘 중 하나를 "Desktop"으로 취급합니다. 즉, "컴퓨터"하위 항목을 볼 수 있으며 해당 항목을 열어 개별 드라이브 문자를 볼 수 있습니다. .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 프리 릴리즈에서 수행 될 수 있습니다.


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를 참조해야합니다.


This link has a simple answer that worked for me fine (I have windows 8.1)

FolderBrowserDialog: Expanding the selected directory


The best approach, at least the most reliable is to make your own browser class dialog box. The tree scrolling issue has been a pain for many years - it will never get fixed!

If you know how to render in paint there is not much you can't do.. fast in paint well that is another story.

The first place I would look is at the open source .Net source code on GitHub, in your .Net version of choice, for the dialog class you're interested in improving. You may be surprised what you can achieve with a little effort and follow through. Just duplicate the control and debug to the point where the error occurs and patch - that'a what Microsoft does, so too can you!

Since this is an old thread and posting samples may never get read. It would make more since to post if asked.

Yet for someone looking to solve such an issue as with tree scrolling to the "expected" directory, here is some solid advise. If an issue exists with a control or library that has no immediate solution, create your own version, when possible extend the original and patch the problem. I've revamped everything from the Windows.Form.Control class to Win32 libraries for the sole purpose of getting predictable and accurate results.

The good news is that with C# there is a lot of low level control available to achieve almost any reasonable objective and the is C too.

In the past I have spent way too many hours searching for a solution to a problem where had I just recreated what was not working a lot of time would have been saved.

참고 URL : https://stackoverflow.com/questions/6942150/why-folderbrowserdialog-dialog-does-not-scroll-to-selected-folder

반응형