Чому діалогове вікно 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, якщо ви хочете продовжити це.
Ганс Пасант,

Відповіді:


86

Принциповою проблемою є неправильне дизайнерське рішення в FolderBrowserDialog. По-перше, нам слід усвідомити, що FolderBrowserDialogце не є .NET-управлінням, а скоріше є Common Dialogі є частиною Windows. Дизайнер цього діалогового вікна вирішив не надсилати елемент керування TreeView aTVM_ENSUREVISIBLE повідомлення після того, як відобразиться діалогове вікно та буде вибрана початкова папка. Це повідомлення змушує прокручувати елемент керування TreeView так, щоб вибраний на даний момент елемент був видно у вікні.

Таким чином, все , що нам потрібно зробити , щоб виправити це , щоб відправити TreeView , який є частиною FolderBrowserDialogв TVM_ENSUREVISIBLEповідомленні , і все буде чудово. Правда? Ну, не так швидко. Це справді відповідь, але деякі речі перешкоджають нам.

  • По-перше, оскільки FolderBrowserDialogнасправді не є елементом керування .NET, він не має внутрішньої Controlsколекції. Це означає, що ми не можемо просто знайти та отримати доступ до дочірнього елемента управління TreeView із .NET.

  • По-друге, дизайнери FolderBrowserDialogкласу .NET вирішили опечатати цей клас. Це невдале рішення заважає нам виходити з нього та замінювати віконний обробник повідомлень. Якби ми змогли це зробити, ми могли б спробувати опублікувати TVM_ENSUREVISIBLEповідомлення, коли отримали WM_SHOWWINDOWповідомлення в обробнику повідомлень.

  • Третя проблема полягає в тому, що ми не можемо відправити TVM_ENSUREVISIBLEповідомлення, поки елемент керування Tree View насправді не існує як справжнє вікно, і він не існує, поки ми не викликаємо ShowDialogметод. Однак цей метод блокує, тому ми не матимемо можливості розміщувати своє повідомлення після виклику цього методу.

Щоб обійти ці проблеми, я створив статичний допоміжний клас з одним методом, який можна використовувати для показу а FolderBrowserDialog, і змусить його прокручувати до вибраної папки. Я управляю цим, починаючи короткий шлях Timerбезпосередньо перед викликом ShowDialogметоду діалогу , а потім відстежуючи дескриптор елемента TreeViewкерування в Timerобробнику (тобто після відображення діалогу) і надсилаючи наше TVM_ENSUREVISIBLEповідомлення.

Це рішення не є ідеальним, оскільки воно залежить від деяких попередніх знань про FolderBrowserDialog . Зокрема, я знаходжу діалог, використовуючи назву його вікна. Це призведе до розриву з інсталяціями, що не належать англійській мові. Я відстежую дочірні елементи керування у діалозі, використовуючи їх ідентифікатори елементів діалогу, а не текст заголовка або назву класу, тому що я вважав, що це буде надійніше з часом.

Цей код протестовано на 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
Це слід позначити як відповідь. Я щойно зіткнувся з тим же питанням, і цей код працював ідеально. Це також дуже детальне та добре написане пояснення.
Dan

Яким повинен бути другий аргумент методу ShowFolderBrowser? IWin32Window...?
Syspect

@Syspect - Аргумент IWin32Window - це просто батьківська форма, з якої запускається засіб вибору папок. Якщо ви викликаєте це безпосередньо з коду форми, ви можете просто використовувати ключове слово 'this' як параметр. (Технічно, IWin32Window насправді є обгорткою навколо hWnd за формою, але C # приховує від вас все потворне, пов'язане з цим!)
Бред Естріхер

2
У Win7 я помітив, що прокрутка відбулася, а потім була скинута, оскільки системні папки, такі як Бібліотеки тощо, були додані до дерева після того, як діалогове вікно було спочатку показано. Встановлення початкового інтервалу в 1000 мс було достатньо, щоб подолати це, хоча це лише ще одна карта зверху!
Джонатан Мітчелл,

1
У Win10, як зазначив @Jonathan Mitchell, є проблема з термінами. Встановлення t.Interval = 100; було достатньо, щоб вирішити це на моїй машині (довше для повільніших машин?).
avenmore

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

1
Це було мені найбільш корисно в операційній системі x64 Windows 8. Однак я розширив його, додавши виконання ключів відправлення в події Timer_Tick через 500 мілісекунд, коли він перемістився до вибраної папки, а потім повернувся на кореневий диск цієї папки. Тож потрібна була затримка.
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
Працює для мене. Цікаво дізнатись, що послідовність клавіатур табуляція та вкладка прокручується до вибраного каталогу. У C #:SendKeys.Send("{TAB}{TAB}{RIGHT}");
Роланд

1
"ця помилка": Я вважаю, що "ця" відноситься до хитрості SendKeys, і що "помилка" повинна бути "особливістю".
Роланд

8

на коді VB.Net, просто поставте цей рядок коду безпосередньо перед тим, як показати діалогове вікно.

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

ця відповідь чудова! вирішує мою проблему поки що .. справді ненавиділо прокручувати вниз до вибору ..: +1 :. була б моєю прийнятою відповіддю, якби це залежало від мене: П
Ден Бредбері

На жаль, це не працює в моїй системі (Windows 10);
Філ Роджерс

3

Я читав на різних форумах, що це могло бути пов’язано з RootFolder, оскільки SelectedPath та RootFolder є взаємовиключними, тобто обидва не можуть співіснувати, але за замовчуванням RootFolder (.Desktop), це дозволяє, принаймні, підніматися по Дереву (переміщатися по диску / папки).

Однак, якщо RootFolder замінено на інший, ніж Desktop, ви не зможете перейти до шляхів UNC.

Відповідь Гансу Пассанту: Я спробував це розширення діалогу, в якому є TextBox, але не пощастило.

Налаштування діалогового вікна перегляду папок для відображення шляху


3

це працює для мене

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

але лише після другого використання діалогового вікна


3

Я виявив, що:

  1. Якщо .SelectedPathзакінчується на "\", діалогове вікно прокручується вниз, щоб зробити шлях видимим.
  2. Якщо .SelectedPathне закінчується на "\", шлях все одно вибраний, але не забезпечений видимим.

На жаль, це рішення працює лише вдвічі. Здається, всередині є якийсь перегоновий стан. Примітка: каталог повинен існувати.
Олександр

Я ще не бачив цієї роботи. Відбір завжди залишається в корені.
Філ Роджерс

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


Питання досить давнє, тому я б не покладав особливих сподівань на те, що хтось скоро відповість. Дякуємо за вступ!
ZygD

1
Це слід задавати у своєму власному запитанні. Його тут швидше за все не побачать.
Стюарт Зіглер

2

Я стикаюся з тією ж проблемою в c ++ / mfc. Мені вдалося використовувати :: PostMessage, а не :: SendMessage у зворотному виклику BFFM_INITIALIZED, щоб розмістити повідомлення 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

Я прочитав вищезазначене обговорення та рішення. Особливо Брат Естрихер поставив мене у правильному напрямку. По суті, ми повинні спочатку знайти елемент керування TreeView у SHBrowseForFolderдіалоговому вікні та надіслати цьому вікну 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);
} 

Велике спасибі Гері Біне .


0

dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

не те саме, що

dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

У чому різниця між SpecialFolder.Desktop та SpecialFolder.DesktopDirectory?

Пов’язаний потік вказує, що як шлях вони отримують однаковий результат. Але вони не однакові, оскільки один є логічним шляхом, а інший - фізичним.

Я знайшов, коли будь-який із них присвоєно RootFolder діалогового вікна відкритої папки, результуюча поведінка може бути різною.

Як призначення .RootFolder, деякі версії Windows, наприклад win7, розглядають будь-яку з них як "Робочий стіл". Тобто ви можете побачити підпункт "Комп'ютер" і відкрити його, щоб побачити окремі букви диска. .SelectedPath вибирається в будь-який спосіб, але вибраний шлях стає видимим лише тоді, коли логічний шлях робочого столу призначений .RootFolder.

Гірше того, при використанні діалогового вікна перегляду папок у попередньому випуску win10, здається, що "DesktopDirectory" є лише таким, вмістом лише каталогу Desktop без жодного посилання на логічний каталог робочого столу. І не вказуючи під ним жодних підпунктів. Дуже неприємно, якщо додаток, написаний для win7, намагається використовувати з win10.

Я думаю, що проблема, з якою має ОП, полягає в тому, що вони використовували фізичний робочий стіл як корінь, коли вони мали використовувати логічний робочий стіл.

У мене немає пояснення, чому дві різні машини OP реагують по-різному. Я б припустив, що у них встановлено дві різні версії .NET framework.

Той факт, що передвипуск win10 має проблему "Застряг на робочому столі" з діалоговим вікном перегляду папок, може бути пов'язаний із більш пізньою .NET структурою, що постачається з попередньою версією win10. На жаль, я все ще не знаю всіх фактів у цій справі (win10), оскільки я ще не оновлювався.

PS Я виявив, що win8 також відчуває симптом "Застряг на робочому столі":

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

Обхідний шлях полягав у виборі альтернативного графічного інтерфейсу в win8. Можливо, щось подібне можна зробити в попередньому випуску win10.


0

У відповідь на допис Марка Ф - я перетворив 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


0

Це посилання містить просту відповідь, яка мені добре працювала (у мене є Windows 8.1)

FolderBrowserDialog: Розгортання вибраного каталогу


1
Це розширює вибрану папку, але першу дочірню особу залишає неправильно вибраною і проблему прокрутки не вирішує.
avenmore

Чому ви посилаєтесь на інший сайт, не принаймні копіюючи рішення тут?
yossico

-1

Найкращий підхід, принаймні найнадійніший - створити власне діалогове вікно класу браузера. Проблема прокрутки дерев мучила багато років - вона ніколи не буде виправлена!

Якщо ви знаєте, як зробити в фарбі, ви не можете зробити багато чого ... швидко в фарбі, це вже інша історія.

Перше місце, на яке я б звернув увагу, - це вихідний код із відкритим вихідним кодом .Net на GitHub, у вибраній вами версії .Net, для класу діалогу, який ви зацікавлені в вдосконаленні. Ви можете бути здивовані тим, чого можете досягти, доклавши трохи зусиль і здійснити. Просто продублюйте елемент керування та налагодьте до точки, де виникає помилка, і виправте - це те, що робить Microsoft, так само ви можете!

Оскільки це старий потік, зразки публікації можуть ніколи не прочитатись. З моменту публікації це зробить більше, якщо про це попросять.

Однак для тих, хто хоче вирішити таку проблему, як прокрутка дерева до "очікуваного" каталогу, ось кілька вагомих порад. Якщо проблема з елементом керування або бібліотекою не має негайного вирішення, створіть власну версію, по можливості розширте оригінал та виправте проблему. Я оновив все, починаючи з класу Windows.Form.Control і закінчуючи бібліотеками Win32 з єдиною метою отримання передбачуваних і точних результатів.

Хороша новина полягає в тому, що для C # існує багато засобів контролю низького рівня для досягнення практично будь-яких розумних цілей, і це також є C.

Раніше я витрачав занадто багато годин на пошук рішення проблеми, де б я щойно відтворив те, що не працює, багато часу було б збережено.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.