FolderBrowserDialog in .Net nachbilden

  • VB.NET

Es gibt 39 Antworten in diesem Thema. Der letzte Beitrag () ist von -Franky-.

    FolderBrowserDialog in .Net nachbilden

    Hi,

    Ich habe eine kleines CellPhone ... und möchte meine Bilder häufiger auf den PC hochladen.

    Bisher mache ich das wie folgt:

    VB.NET-Quellcode

    1. 'Get cellphone folder
    2. Dim shell = New ShellClass()
    3. Dim Hwnd As Integer
    4. Dim CellPhoneFolder = shell.BrowseForFolder(Hwnd, "Choose folder", 0, 17)
    5. FromFolder = CellPhoneFolder


    Dann erhalte ich das angehängte Display und kann den DCIM Subfolder auswählen ... (s. Anhang)

    Auf Dauer ist das nervig, weil ich jedesmal diesen Dialog durchlaufen muss. Mein Cell Phone ändert sich nicht jeden zweiten Tag ... und deshalb würde ich den FromFolder gern ohne Dialog befüllen ...

    Das ist aber nicht ganz so einfach: Der Inhalt von FromFolder sieht jedesmal anders aus. Im Debugger wird [System_ComObject] angezeigt.

    Normalerweise finde ich meine USB Festplatte mit:

    VB.NET-Quellcode

    1. 'Get list of USB drive letters
    2. Dim LettersOfUsbDrives As New List(Of Char)
    3. Dim ManagementObjectSearcher As New ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'")
    4. For Each QueryObject As ManagementObject In ManagementObjectSearcher.Get()
    5. For Each DiskPartitionObject As ManagementObject In QueryObject.GetRelated("Win32_DiskPartition")
    6. For Each LogicalDiskObject As ManagementObject In DiskPartitionObject.GetRelated("Win32_LogicalDisk")
    7. LettersOfUsbDrives.Add(LogicalDiskObject("name").ToString(0))
    8. Next
    9. Next
    10. Next


    Das dynamisch angehängte Cell Phone wird damit aber nicht gefunden.

    Irgendwie würde ich gern den Inhalt von "This PC" durchforsten .. aber das scheint mit "normalen" .Net Techniken nicht zu funktionieren.

    Weiß jemand wie man das machen kann ?

    LG
    Peter
    Bilder
    • s 2023-03-13 09-26-416.jpg

      14,46 kB, 315×311, 96 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Da gibt es ja schon eine längere Diskussion zu dem Thema ....

    Ich habe den OriginalCode von @Franky zum Laufen gebracht.

    Ob mir das etwas bringt weiß ich nicht ... denn erstens habe ich keine Ahnung, wie ich den Dialog auf meinen gewünschten Startpfad einstellen soll (Dialog.Setfolder ist ja wohl ein Rückgabewert) .... und zweitens läuft das Dingens ja non-modal. Und so kann ich das nicht verwenden.

    Deine Lösung sollte da schon besser sein. Leider kann ich das nicht von C# nach .Net übersetzen ... irgendwie stört sich der Converter an dem "enum ... : int" (s. Anhang). Wenn ich das ändere, kommen andere Fehler ...

    Irgendwie komme ich nicht weiter.

    LG
    Peter
    Bilder
    • s 2023-03-13 15-09-498.jpg

      114,67 kB, 1.648×626, 95 mal angesehen

    Peter329 schrieb:

    "enum ... : int"
    Dann lass das doch einfach weg., das ist eh Default.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Peter329 schrieb:

    "enum ... : int"
    Dann lass das doch einfach weg., das ist eh Default.


    Na klar, hab ich das wegelassen ... wie schon geschrieben. Aber dann erhalte ich andere Fehler: (s. Anhang)

    Error message:
    System.ArgumentOutOfRangeException: Exception of type 'System.ArgumentOutOfRangeException' was thrown. Parameter name: p

    Also wenn man das Coding nicht versteht ist das schon ein bissl blöde, wenn die Konvertierung nicht läuft ... Ich fühle mich etwas überfordert ...

    LG
    Peter
    Bilder
    • s 2023-03-13 15-55-307.jpg

      85,41 kB, 1.372×683, 94 mal angesehen
    Convert.Net (Offline, siehe meine Signatur) hat anstandslos konvertiert:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Windows.Forms
    2. Imports System.Runtime.InteropServices
    3. Imports System.ComponentModel
    4. Imports System
    5. Namespace TestPickFolderDialog
    6. <DesignerCategory("code")> _
    7. Public Class PickFolderDialog
    8. Inherits Component
    9. #Region "Const"
    10. Private Const S_OK As Integer = 0
    11. Private Const IID_IModalWindow As String = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802"
    12. Private Const IID_IFileDialog As String = "42f85136-db7e-439c-85f1-e4075d135fc8"
    13. Private Const IID_IShellItem As String = "43826d1e-e718-42ee-bc55-a1e261c37bfe"
    14. Private Const CLSID_FileOpenDialog As String = "dc1c5a9c-e88a-4dde-a5a1-60f82a20aef7"
    15. #End Region
    16. #Region "Enums"
    17. Private Enum FileOpenDialogOptions As Integer
    18. OverwritePrompt = &H2
    19. FOS_STRICTFILETYPES = &H4
    20. FOS_NOCHANGEDIR = &H8
    21. FOS_PICKFOLDERS = &H20
    22. FOS_FORCEFILESYSTEM = &H40
    23. FOS_ALLNONSTORAGEITEMS = &H80
    24. FOS_NOVALIDATE = &H100
    25. FOS_ALLOWMULTISELECT = &H200
    26. FOS_PATHMUSTEXIST = &H800
    27. FOS_FILEMUSTEXIST = &H1000
    28. FOS_CREATEPROMPT = &H2000
    29. FOS_SHAREAWARE = &H4000
    30. FOS_NOREADONLYRETURN = &H8000
    31. FOS_NOTESTFILECREATE = &H10000
    32. FOS_HIDEMRUPLACES = &H20000
    33. FOS_HIDEPINNEDPLACES = &H40000
    34. FOS_NODEREFERENCELINKS = &H100000
    35. FOS_OKBUTTONNEEDSINTERACTION = &H200000
    36. FOS_DONTADDTORECENT = &H2000000
    37. FOS_FORCESHOWHIDDEN = &H10000000
    38. FOS_DEFAULTNOMINIMODE = &H20000000
    39. FOS_FORCEPREVIEWPANEON = &H40000000
    40. FOS_SUPPORTSTREAMABLEITEMS = Integer.MinValue + &H0
    41. End Enum
    42. Public Enum SIGDN As Integer
    43. SIGDN_NORMALDISPLAYField = &H0
    44. SIGDN_PARENTRELATIVEPARSINGField = Integer.MinValue + &H18001
    45. SIGDN_DESKTOPABSOLUTEPARSINGField = Integer.MinValue + &H28000
    46. SIGDN_PARENTRELATIVEEDITINGField = Integer.MinValue + &H31001
    47. SIGDN_DESKTOPABSOLUTEEDITINGField = Integer.MinValue + &H4c000
    48. SIGDN_FILESYSPATHField = Integer.MinValue + &H58000
    49. SIGDN_URLField = Integer.MinValue + &H68000
    50. SIGDN_PARENTRELATIVEFORADDRESSBARField = Integer.MinValue + &H7c001
    51. SIGDN_PARENTRELATIVEField = Integer.MinValue + &H80001
    52. SIGDN_PARENTRELATIVEFORUIField = Integer.MinValue + &H94001
    53. End Enum
    54. #End Region
    55. #Region "Member"
    56. Private FileDialog As IFileDialog
    57. #End Region
    58. #Region "API"
    59. <DllImport("Shell32.dll", EntryPoint := "SHCreateItemFromParsingName")> _
    60. <PreserveSig> _
    61. Private Shared Function SHCreateItemFromParsingName(<[In]> <MarshalAs(UnmanagedType.LPWStr)> pszPath As String, <[In]> pbc As IntPtr, <[In]> <MarshalAs(UnmanagedType.LPStruct)> riid As Guid, ByRef pUnk As IntPtr) As Integer
    62. End Function
    63. #End Region
    64. #Region "Properties"
    65. Public Property SelectedPath() As String
    66. Get
    67. Return m_SelectedPath
    68. End Get
    69. Set
    70. m_SelectedPath = Value
    71. End Set
    72. End Property
    73. Private m_SelectedPath As String
    74. #End Region
    75. #Region "Konstruktor"
    76. Public Sub New()
    77. Dim options As FileOpenDialogOptions
    78. Me.FileDialog = CType(Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_FileOpenDialog))), IFileDialog)
    79. If Me.FileDialog Is Nothing Then
    80. Throw New COMException("PickFolderDialog constructor failed.")
    81. End If
    82. If Me.FileDialog.GetOptions(options) = S_OK Then
    83. options = options Or FileOpenDialogOptions.FOS_PICKFOLDERS
    84. Me.FileDialog.SetOptions(options)
    85. End If
    86. End Sub
    87. #End Region
    88. #Region "Public Functions"
    89. Public Function ShowDialog(hwndOwner As IntPtr) As DialogResult
    90. Me.SetFolder(Me.SelectedPath)
    91. If Me.FileDialog.Show(hwndOwner) = S_OK Then
    92. Me.SelectedPath = Me.GetResult(PickFolderDialog.SIGDN.SIGDN_FILESYSPATHField)
    93. Return DialogResult.OK
    94. End If
    95. Return DialogResult.Cancel
    96. End Function
    97. Public Function GetResult(Optional eSIGDN As SIGDN = SIGDN.SIGDN_FILESYSPATHField) As String
    98. Dim strRet As String = String.Empty
    99. If Me.FileDialog.GetResult(IShellItem) Then
    100. ShellItem
    101. End If
    102. Marshal.FreeCoTaskMem(pszName)
    103. End Function
    104. End Class
    105. End Namespace

    ... ob es auch läuft, habe ich nicht getestet.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „FormFollowsFunction“ ()

    @Peter329 Klink mal diesen Konverter in Dein Studio ein:
    marketplace.visualstudio.com/i…DevelopTeam.CodeConverter
    @FormFollowsFunction Mach da mal nen Spoiler drum herum.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Peter329 So ein ähnliches Thema hatten wir hier auch schon. Evtl. hilft Dir das COM-Interface IPortableDeviceManager weiter. Siehe hier: Cell Phone als USB device und nachfolgende. Was Du im Endeffekt benötigst ist die DeviceID die dann zu einem Pfad zusammengebaut wird. z.B. -> "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\" & strDeviceID & "\Phone\DCIM".
    "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\" = "This PC". strDeviceID = DeviveID deines Handy. "\Phone\DCIM" der restliche Pfad zu Deinem DCIM-Ordner den Du entsprechend anpassen musst.

    Ich fasse das ganze mal grob zusammen wie man das angehen könnte: GeräteID über IPortableDeviceManager ermitteln. Pfad "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\" & DeviceID" dem IFileOpenDialog (MultiSelect) -> SetFolder oder SetDefaultFolder übergeben. Dann bist schon mal direkt auf dem Gerät und musst noch zum DCIM Ordner navigieren. Dateien, die kopiert werden sollen, auswählen. IFileOpenDialog.GetResults liefert Dir ein IShellItemArray zurück. Dieses kannst direkt mit Dateien und Ordner löschen, kopieren, verschieben und umbenennen per IFileOperation zum kopieren weiterverarbeiten.

    Falls Du mit den ganzen COM-Interfaces nicht zurecht kommst, es gibt da ein MicrosoftApiCodePack wo die meisten COM-Interfaces für .NET schön in Klassen/Namespaces verpackt sind. Habe ich aber nie verwendet.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „-Franky-“ ()

    Inzwischen habe ich einen Konverter gefunden, der den Code von RFG (etappenweise) übersetzt ... (Das ist Version 1.1 aus deinem Post)

    So rufe ich das Dingens auf:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    3. If Me.pickFolderDialog1.ShowDialog(Me.Handle) = DialogResult.OK Then
    4. ' this.pickFolderDialog1 ist die Instanz, die im Designer auf die GUI gezogen wurde
    5. Me.Label1.Text = Me.pickFolderDialog1.SelectedPath
    6. End If
    7. End Sub
    8. End Class



    Allerdings erhalte ich einen Fehler, wenn ich den PickFolderDialog auf die GUI ziehe ... (s. Anhang)

    Was mache ich denn da falsch ? Irgendwie habe ich keine so rechte Ahnung, wie ich das im Debugger tracen könnte - ich hab mal einen Haltepunkt auf den Konstruktor gesetzt aber der kommt nicht zum Tragen ...

    LG
    Peter
    Bilder
    • s 2023-03-13 20-42-512.jpg

      55,38 kB, 1.440×390, 83 mal angesehen

    Peter329 schrieb:

    Allerdings erhalte ich einen Fehler, wenn ich den PickFolderDialog auf die GUI ziehe ... (s. Anhang)
    Was mache ich denn da falsch ?
    Da machst du nix falsch.
    Offsichtlich ist das picFolderDialog-Control buggy.
    Einerseits sollte ein (selbstgeschriebenes) .Net-WinForms-Control sich problemlos aus der Toolbox aufs Form ziehen lassen.
    Andererseits ists garnet einfach, das korrekt hinzukriegen.
    @Franky

    Also ich möchte eigentlch nur den FolderBrowserDialog dahingehend abändern, dass ich mir mein CellPhone merken kann und dann beim nächsten Aufruf gleich den entsprechenden Ordner aufmache. (s. mein erstes Posting).

    Wie ich das jetzt erreiche, da bin ich leidenschaftslos.

    Deinen Code habe ich zum Laufen gebracht. Der funktioniert ja auch mit Using .... ganz prima ! Aber ich bin damit aus zwei Gründen noch nicht happy:

    1. Das Dingens wird non-modal gestartet. Das ist für meine Zwecke nicht das richtige ... Ich brauche einen modalen Dialog.

    2. Ich sehe nicht, wie und wo und wie ich mir in deinem Code den ausgewählten Ordner merken kann ... und wie ich diesen Ordner beim nächsten Start wieder automatisch zuordnen kann.

    Ich hoffe, ich habe mein Problem klar machen können.

    Um es klar zu sagen: meine bisherige Lösung mit dem Standard .Net Dialog funktioniert gut ... ich muss halt nur bei jedem Start mein Cell Phone erneut auswählen ... und das würde ich gern vermeiden. Nur darum geht es mir.

    LG
    Peter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    @Peter329 Den leicht verstaubten FolderBrowserDialog nutze ich seit WinVista nicht mehr wirklich. Ansonsten Pfad beim ersten Auswählen merken und irgendwo in den Settings speichern. Beim erneuten starten der App, Pfad aus den Settings auslesen und dem FolderBrowserDialog als StartFolder übergeben. Was ich jetzt nicht weis ist, keinen Pfad vorgeben und der FolderBrowserDialog merkt sich von allein den zuletzt ausgewählten Ordner und navigiert von sich aus dort hin.

    Peter329 schrieb:

    Deinen Code habe ich zum Laufen gebracht. Der funktioniert ja auch mit Using .... ganz prima ! Aber ich bin damit aus zwei Gründen noch nicht happy:
    1. Das Dingens wird non-modal gestartet. Das ist für meine Zwecke nicht das richtige ... Ich brauche einen modalen Dialog.
    2. Ich sehe nicht, wie und wo und wie ich mir in deinem Code den ausgewählten Ordner merken kann ... und wie ich diesen Ordner beim nächsten Start wieder automatisch zuordnen kann.

    Zu 1. Die Dialoge über das Interface IFileDialog können beides. Jenachdem ob Du ein hWnd angibts oder nicht.
    Zu 2. Die Dialoge über das Interface IFileDialog merken sich automatisch den zuletzt ausgewählten Ordner wenn kein StartFolder vorgegeben wird. Ansonsten wie beim FolderBrowserDialog. Letzten ausgewählten Ordner merken und speichern, wieder einlesen und entsprechend den StartFolder vorgeben.
    Mfg -Franky-
    Also .... ich versuche jetzt Deinen Vorschlag mit dem IFileDialog umzusetzen.

    Da ich IFileDialog nicht in der ToolBox finde, nehme ich an, dass ich deinen PickFolderDialog verwenden muss. Ich habe den mal in PicFolderDialog2 umbenannt.

    Der Aufruf

    VB.NET-Quellcode

    1. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    2. Using Dialog As New PickFolderDialog2
    3. If Dialog.SetFolder Then
    4. If Dialog.Show Then
    5. Debug.Print(Dialog.GetResult(PickFolderDialog2.SIGDN.SIGDN_FILESYSPATH))
    6. End If
    7. End If
    8. End Using
    9. End Sub


    ist non-modal. Ich brauche aber einen modalen Aufruf. Nur gibt es hier keine Methode .ShowDialog. Die müsste man hinzufügen.

    So habe ich das versucht:

    VB.NET-Quellcode

    1. Public Function ShowDialog(Optional hwndOwner As IntPtr = Nothing) As Boolean
    2. Dim bolRet As Boolean = False
    3. If m_FileOpenDialog IsNot Nothing Then
    4. If CType(m_FileOpenDialog, IFileDialog).ShowDialog(hwndOwner) = S_OK Then
    5. bolRet = True
    6. End If
    7. End If
    8. Return bolRet
    9. End Function


    Das liefert aber einen Fehler, weil IFileDialog die Methode .ShowDialog nicht kennt.

    Das ist schon die erste Hürde beim Umsetzen deiner Lösung.

    LG
    Peter

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Peter329“ ()

    @Peter329 Wir machen mal folgendes. Lade Dir das Bsp. von hier herunter: OpenFileDialog, SaveFileDialog, PickFolderDialog (Single Select) per Interface IFileDialog verwenden
    Das ganze ist nur eine Klasse, kein Control das Du Dir auf die Form ziehen kannst. Klicke auf Button "PickFolderDialog". Navigiere zu Deinem Handy und wähle den DCIM-ordner aus und klicke auf den Button "Ordner auswählen". Öffne den PickFolderDialog wieder. Dein zuletzt ausgewählter Ordner sollte nun wieder angezeigt werden ohne das Du einen StartFolder vorgibst.
    Mfg -Franky-
    Also dein Beispiel funktioniert auf Anhieb ! Der zuletzt ausgewählte Ordner wird beim nächsten Aufruf restauriert. Ich bin begeistert !

    Jetzt muss ich noch sehen, wie ich die entsprechenden Code Teile zu meinen Zwecken in mein Projekt packe ! Da bin ich aber guter Hoffnung, dass ich das hinbekomme.

    Recht herzlichen Dank für deine Hilfe !

    LG
    Peter

    Peter329 schrieb:

    Jetzt muss ich noch sehen, wie ich die entsprechenden Code Teile zu meinen Zwecken in mein Projekt packe !

    Einfach die Klasse FileDialog.vb zu Deinem Projekt hinzufügen und den Code von Button4 verwenden. Mehr brauchst Du nicht wenn Du die Events vom Dialog nicht benötigst. ;)
    Mfg -Franky-

    Peter329 schrieb:

    ich muss halt nur bei jedem Start mein Cell Phone erneut auswähle
    ich weiss nicht, bei welchem komischen Dialog du derzeit stehst.
    Aber kann man nicht den .Net-winforms-Standard-Dialog nehmen, aufs Form ziehen, und sein .SelectedPath einstellen?
    Dann sollte er genau da öffnen, ohne gross rumzuklicksen.
    Dialoge benutzen ist einfach
    Dachte ich auch, aber die Dialoge akzeptieren keinen entsprechenden Handyordner. Zumindest bei mir werden alle Versuche, egal, ob .NET-Framework-Dialog, Shell-Dialog, oder über WinAPI-Funktion mit einer entsprechenden Fehlermeldung quittiert.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.