81 votes

Pourquoi la boîte de dialogue FolderBrowserDialog ne défile pas vers le dossier sélectionné ?

Comme le montre cette capture d'écran, le dossier sélectionné n'est pas dans la vue. Il faut faire défiler l'écran vers le bas pour voir le dossier sélectionné.

enter image description here

La même boîte de dialogue montre le dossier sélectionné visible sur un autre ordinateur

enter image description here

Je l'ai fait fonctionner sur deux ordinateurs ayant tous deux Windows 7. Il fonctionne correctement sur l'un mais pas sur le second. Il semble qu'il s'agisse d'un problème lié à l'environnement Windows ou à un problème de code. Quelqu'un peut-il suggérer une solution ?

Il n'y a pas de changement dans le code. J'ai utilisé des chemins plus longs à partir de différents lecteurs mais les résultats sont les mêmes.

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

        }

91voto

Brad Oestreicher Points 211

Le problème fondamental est une mauvaise décision de conception dans la FolderBrowserDialog . Tout d'abord, nous devons réaliser que le FolderBrowserDialog n'est pas un contrôle .NET, mais plutôt l'interface utilisateur de l Common Dialog et fait partie de Windows. Le concepteur de cette boîte de dialogue a choisi de ne pas envoyer au contrôle TreeView un message TVM_ENSUREVISIBLE après l'affichage de la boîte de dialogue et la sélection d'un dossier initial. Ce message provoque le défilement d'un contrôle TreeView afin que l'élément actuellement sélectionné soit visible dans la fenêtre.

Donc, tout ce que nous devons faire pour résoudre ce problème est d'envoyer le TreeView qui fait partie de l'interface de l'utilisateur. FolderBrowserDialog el TVM_ENSUREVISIBLE et tout sera parfait. Pas vrai ? Eh bien, pas si vite. C'est en effet la réponse, mais il y a des choses qui se dressent sur notre chemin.

  • Premièrement, parce que le FolderBrowserDialog n'est pas vraiment un contrôle .NET, il n'a pas d'interface interne. Controls collection. Cela signifie que nous ne pouvons pas simplement trouver et accéder au contrôle enfant TreeView à partir de .NET.

  • Deuxièmement, les concepteurs du système .NET FolderBrowserDialog La classe a décidé de joint cette classe. Cette décision malheureuse nous empêche de dériver de cette classe et de surcharger le gestionnaire de messages de la fenêtre. Si nous avions été en mesure de le faire, nous aurions pu essayer d'afficher la classe TVM_ENSUREVISIBLE lorsque nous avons reçu le message WM_SHOWWINDOW dans le gestionnaire de messages.

  • Le troisième problème est que nous ne pouvons pas envoyer la TVM_ENSUREVISIBLE tant que le contrôle Tree View n'existe pas en tant que fenêtre réelle, et il n'existe pas tant que nous n'appelons pas la commande ShowDialog méthode. Cependant, cette méthode se bloque, nous n'aurons donc pas la possibilité de poster notre message une fois que cette méthode sera appelée.

Pour contourner ces problèmes, j'ai créé une classe d'aide statique dotée d'une seule méthode qui peut être utilisée pour afficher un message de type FolderBrowserDialog et le fera défiler jusqu'au dossier sélectionné. Je gère cela en lançant une courte Timer juste avant d'appeler la fonction ShowDialog puis en recherchant la poignée de la méthode TreeView contrôle dans le Timer (c'est-à-dire après l'affichage du dialogue) et en envoyant notre TVM_ENSUREVISIBLE message.

Cette solution n'est pas parfaite car elle dépend de certaines connaissances préalables sur la FolderBrowserDialog . Plus précisément, je trouve le dialogue en utilisant le titre de sa fenêtre. Cela ne fonctionnera pas avec les installations non anglaises. Je retrouve les contrôles enfants dans le dialogue en utilisant leur ID d'élément de dialogue, plutôt que le texte du titre ou le nom de la classe, car j'ai pensé que cela serait plus fiable dans le temps.

Ce code a été testé sur Windows 7 (64 bit), et Windows XP.

Voici le code : (Vous pouvez avoir besoin : 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;
    }
}

11voto

Loi Condes Points 87

Sur le code VB.Net, il suffit de mettre cette ligne de code juste avant de montrer le dialogue.

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

11voto

Brien Halstead Points 66

Je sais que ce fil de discussion est BEAUCOUP vieux, mais avec les méthodes d'extension, ceci peut être ajouté à la méthode FolderBrowserDialog.ShowDialog, et ensuite utilisé à plusieurs reprises là où c'est nécessaire.

L'exemple (ci-dessous) utilise simplement la méthode simple SendKeys (que je déteste faire, mais dans ce cas, cela fonctionne bien). Lorsque vous utilisez la méthode SendKeys pour accéder au dossier sélectionné dans la boîte de dialogue, si vous déboguez dans Visual Studio, l'appel SendKeys s'applique à la fenêtre actuelle, c'est-à-dire la fenêtre VS active. Pour être plus sûr et éviter que la mauvaise fenêtre ne reçoive le message SendKeys, la méthode d'extension contiendrait les appels de méthode externes pour envoyer des messages à la fenêtre spécifique, comme ce que Marc F a publié, mais traduit en 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);
    }
}

8voto

Tobias Knauss Points 63

J'ai utilisé une solution de contournement de 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 ();

Ce n'est pas la manière la plus agréable, mais ça marche pour moi.
Sans le RootFolder cela ne fonctionne PAS au premier appel, mais au 2ème et aux suivants. Avec elle, elle fonctionne toujours.

Comme d'autres l'ont observé, cet échec dépend du système d'exploitation :
J'utilise Win 7 Pro x64 SP1.

5voto

user3458105 Points 11

Cela fonctionne pour moi

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

mais seulement après la deuxième utilisation du dialogue

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X