FolderBrowserDialog in .Net nachbilden

  • VB.NET

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

    @VaporiZed
    -> egal, ob .NET-Framework-Dialog, Shell-Dialog, oder über WinAPI-Funktion mit einer entsprechenden Fehlermeldung quittiert.

    Die API SHBrowseForFolder sollte das können. Bei BROWSEINFO.pidlRoot kann ja eine PIDL, die z.B. von SHParseDisplayName -> SHGetIDListFromObject kommt, übergeben werden. SHParseDisplayName kann entsprechende Ordner verarbeiten. Aber: learn.microsoft.com/en-us/wind…hbrowseforfoldera#remarks

    Wie schaut es eigentlich in .NET5/6/7 aus? Ich hab mir das noch garnicht großartig angeschaut. Gibt es da auch noch die alten Dialoge? UWP-Apps z.B. verwenden ebenfalls den FolderPicker: learn.microsoft.com/en-us/uwp/…erpicker?view=winrt-22621

    Edit: Nur der Vollständigkeit Halber wie man es evtl. mit den alten Dialog machen könnte (Subclassing des Dialoges hab ich weggelassen). Was man halt braucht ist die DeviceID des Handy (in diesem Bsp ist meine DeviceID vom Handy zu sehen!) oder man navigiert einmalig zum entsprechenden Handy-Ordner, wählt diesen aus und nimmt den zurückgelieferten Pfad für den RootFolder.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Runtime.InteropServices
    4. Public Class Form1
    5. Private Const S_OK As Integer = 0
    6. Private Const MAX_PATH As Integer = 512
    7. Private Const IID_IShellItem As String = "43826d1e-e718-42ee-bc55-a1e261c37bfe"
    8. Private Enum BROWSEINFOFLAGS As UInteger
    9. BIF_RETURNONLYFSDIRS = 1
    10. BIF_DONTGOBELOWDOMAIN = 2
    11. BIF_STATUSTEXT = 4
    12. BIF_RETURNFSANCESTORS = 8
    13. BIF_EDITBOX = 16
    14. BIF_VALIDATE = 32
    15. BIF_NEWDIALOGSTYLE = 64
    16. BIF_BROWSEINCLUDEURLS = 128
    17. BIF_UAHINT = 256
    18. BIF_DONTSHOWNEWFOLDERBUTTON = 512
    19. BIF_USENEWUI = BIF_EDITBOX Or BIF_NEWDIALOGSTYLE
    20. BIF_BROWSEFORCOMPUTER = 4096
    21. BIF_BROWSEFORPRINTER = 8192
    22. BIF_BROWSEINCLUDEFILES = 16384
    23. BIF_SHAREABLE = 32768
    24. BIF_BROWSEFILEJUNCTIONS = 65536
    25. End Enum
    26. Private Enum SIGDN As Integer
    27. SIGDN_NORMALDISPLAY = &H0
    28. SIGDN_PARENTRELATIVEPARSING = &H80018001
    29. SIGDN_DESKTOPABSOLUTEPARSING = &H80028000
    30. SIGDN_PARENTRELATIVEEDITING = &H80031001
    31. SIGDN_DESKTOPABSOLUTEEDITING = &H8004C000
    32. SIGDN_FILESYSPATH = &H80058000
    33. SIGDN_URL = &H80068000
    34. SIGDN_PARENTRELATIVEFORADDRESSBAR = &H8007C001
    35. SIGDN_PARENTRELATIVE = &H80080001
    36. SIGDN_PARENTRELATIVEFORUI = &H80094001
    37. End Enum
    38. Private Structure BROWSEINFO
    39. Dim hwndOwner As IntPtr
    40. Dim pidlRoot As IntPtr
    41. <MarshalAs(UnmanagedType.LPTStr)> Dim pszDisplayName As String
    42. <MarshalAs(UnmanagedType.LPTStr)> Dim lpszTitle As String
    43. Dim ulFlags As BROWSEINFOFLAGS
    44. Dim lpfn As IntPtr
    45. Dim lParam As IntPtr
    46. Dim iImage As Integer
    47. End Structure
    48. <DllImport("shell32.dll", EntryPoint:="SHBrowseForFolderW")>
    49. Private Shared Function SHBrowseForFolder(<MarshalAs(UnmanagedType.Struct)> ByRef lpFileOp As BROWSEINFO) As IntPtr
    50. End Function
    51. <DllImport("Shell32.dll", EntryPoint:="SHCreateItemFromParsingName")>
    52. <PreserveSig> Private Shared Function SHCreateItemFromParsingName(<[In], MarshalAs(UnmanagedType.LPWStr)> pszPath As String,
    53. <[In]> pbc As IntPtr,
    54. <[In], MarshalAs(UnmanagedType.LPStruct)> riid As Guid,
    55. <Out> ByRef pUnk As IntPtr) As Integer
    56. End Function
    57. <DllImport("Shell32.dll", EntryPoint:="SHGetIDListFromObject")>
    58. <PreserveSig> Private Shared Function SHGetIDListFromObject(<[In]> pUnk As IntPtr,
    59. <Out> ByRef ppidl As IntPtr) As Integer
    60. End Function
    61. <DllImport("Shell32.dll", EntryPoint:="SHCreateShellItem")>
    62. <PreserveSig> Private Shared Function SHCreateShellItem(<[In]> pidlParent As IntPtr,
    63. <[In]> psfParent As IntPtr,
    64. <[In]> pidl As IntPtr,
    65. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppsi As IShellItem) As Integer
    66. End Function
    67. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    68. '::{20D04FE0-3AEA-1069-A2D8-08002B30309D} = ThisPC
    69. '\\?\usb#vid.... = DeviceID vom Handy
    70. Dim RootFolder As String = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_04e8&pid_6860&ms_comp_mtp&samsung_android#7&122e7c35&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}"
    71. If String.IsNullOrEmpty(RootFolder) Then RootFolder = Char.MinValue
    72. Dim pIShellItem As IntPtr
    73. If SHCreateItemFromParsingName(RootFolder, IntPtr.Zero,
    74. New Guid(IID_IShellItem),
    75. pIShellItem) = S_OK Then
    76. Dim RootFolderPIDL As IntPtr
    77. If SHGetIDListFromObject(pIShellItem,
    78. RootFolderPIDL) = S_OK Then
    79. Dim BI As New BROWSEINFO
    80. With BI
    81. .hwndOwner = Me.Handle
    82. .pidlRoot = RootFolderPIDL
    83. .pszDisplayName = "DisplayName"
    84. .lpszTitle = "Title"
    85. End With
    86. Dim SelectedFolderPIDL As IntPtr = SHBrowseForFolder(BI)
    87. If SelectedFolderPIDL <> IntPtr.Zero Then
    88. Dim IShellItemSelectedFolder As IShellItem = Nothing
    89. If SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, SelectedFolderPIDL, IShellItemSelectedFolder) = S_OK Then
    90. If IShellItemSelectedFolder IsNot Nothing Then
    91. Dim pwString As IntPtr
    92. If IShellItemSelectedFolder.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, pwString) = S_OK Then
    93. If pwString <> IntPtr.Zero Then
    94. Debug.Print(Marshal.PtrToStringUni(pwString))
    95. Marshal.FreeCoTaskMem(pwString)
    96. End If
    97. End If
    98. Marshal.ReleaseComObject(IShellItemSelectedFolder)
    99. End If
    100. End If
    101. Marshal.FreeCoTaskMem(SelectedFolderPIDL)
    102. End If
    103. Marshal.FreeCoTaskMem(RootFolderPIDL)
    104. End If
    105. Marshal.Release(pIShellItem)
    106. End If
    107. End Sub
    108. <ComImport>
    109. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    110. <Guid(IID_IShellItem)>
    111. Private Interface IShellItem
    112. <PreserveSig> Function BindToHandler() As Integer ' Dummy
    113. <PreserveSig> Function GetParent() As Integer ' Dummy
    114. <PreserveSig> Function GetDisplayName(<[In]> sigdnName As SIGDN,
    115. <Out> ByRef ppszName As IntPtr) As Integer
    116. <PreserveSig> Function GetAttributes() As Integer ' Dummy
    117. <PreserveSig> Function Compare() As Integer ' Dummy
    118. End Interface
    119. End Class

    Mfg -Franky-

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

    -Franky- schrieb:

    Wie schaut es eigentlich in .NET5/6/7 aus?


    Also unter WPF sieht es sogar duster aus. Da haben wir zwar den Microsoft.Win32.OpenFileDialog, aber nichts um Ordner auszuwählen. Microsoft.Win32.OpenFileDialog hat zwar Options, aber nur Readonly. Die Flags für die Options sind ja identisch mit den FOS_ Konstanten(FILEOPENDIALOGOPTIONS) laut Microsoft. Verweis in WPF auf Forms? Nein, danke. WinAPI geht, aber mit den Dialog-Ínterfaces ist IMO am besten, weil der Dialog der mit SHBrowseForFolder erzeugt wird den Stil des Systems ignoriert(Hell/Dunkel). Deshalb bau ich das auch in C#(fehlen nur noch XmlComments), schon mit IShellItem2 und auch eigens implementierten HRESULT.

    Im nachhinein interessant deine implementation zu sehen, bzw, wie du eine Instanz bekommst. Ich hab eine leere "CoClass", diese gebe ich bei den Attributen an.
    z.B. [ComImport(), Guid(IIDs.IID_IFileOpenDialog), CoClass(typeof(OpenFileDialogCoClass))]
    die CoClass hat dann diese Attribute:
    [ComImport(), Guid(CLSIDs.CLSID_FileOpenDialog), ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate)]
    Um dann eine Instanz zu bekommen, mach ich IOpenFileDialog dialog = new IOpenFileDialog(), IOpenFileDialog ist dabei das Interface das IFileOpenDialog implementiert und die CoClass in den Attributen hat.

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „DTF“ ()

    DTF schrieb:

    Also unter WPF sieht es sogar duster aus.
    Oha, hätte ich jetzt nicht gedacht das es unter WPF nichts vergleichbares gibt.

    DTF schrieb:

    weil der Dialog der mit SHBrowseForFolder erzeugt wird den Stil des Systems ignoriert(Hell/Dunkel)

    Nun ja, der alte Dialog per SHBrowseForFolder stammt noch aus Zeiten von vor WinVista.

    DTF schrieb:

    Im nachhinein interessant deine implementation zu sehen

    Egal wie Du die Instanz erzeugst, im Endeffekt wird die Instanz per API CoCreateInstance erstellt. Ist halt nur nicht zu sehen.

    loeffel schrieb:

    In WinForms in .NET haben wir den aktuellen FolderDialog implementiert.

    Nur diesen oder auch alle anderen Dialoge die auf IFileDialog / IFileDialog2 / IFileOpenDialog / IFileSaveDialog basieren. Speziel IFileDialogCustomize wäre sicher von Interesse. Ich habe mir jetzt nicht das ganze Video angeschaut und bin selbt auch noch nicht dazu gekommen mir NET5/6/7 genauer anzuschauen. Den TaskDialog gibt es ja seit Net5. Sind denn weitere neue Standard-Dialoge und andere COM-Interfaces, z.B. aus dem WindowsAPICodePack, in WinForms eingeflossen oder werden noch implementiert? Wie z.B. IPreviewHandler, IFileOperation, IProgressDialog, IOperationsProgressDialog usw? Ist eventuel auch geplant Teile von WinRT in WinForms einfließen zu lassen? z.B. Windows.Media.Ocr oder Windows.Data.Pdf?
    Mfg -Franky-

    -Franky- schrieb:

    Oha, hätte ich jetzt nicht gedacht das es unter WPF nichts vergleichbares gibt.


    Ich war deswegen auch verwundert. Gab mal eine Zeit in der ich dachte WPF wäre die Zukunft für NET, aber so wie WinForms aufpoliert wird glaube ich das nicht mehr. Bin schon neugierig auf NET 9. Frag mich schon wann Animationen mit Timeline in Forms kommen, oder 3D-Rendering.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    WPF ist leider nur für Windows, da werden sich die Multiplatfrorm-Frameworks sicherlich eher durchsetzen. Hab mich auch schon ein wenig mit MAUI beschäftigt, so ganz überzeugt bin ich noch nicht davon, aber da steckt durchaus Potenzial drin. Handy-Apps kann ich mir jedenfalls vorstellen damit zu entwickeln. Mit Android Studio macht das ja wirklich wenig Spaß, allein das neue System für den Dateizugriff bzw. die Rechteverwaltung(Anforderung) ist schrecklich(soll mehr Sicherheit bringen, tuts aber nicht wirklich, verkompliziert alles nur für den Entwickler). Je nach API-Version unterschiedlich zu machen, da kann man kaum den überblick behalten.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Mir würde es schon reichen wenn in WinForms, oder allgemein in NET, zumindes XAML oder WinRT, in welcher Form auch immer, einzug halten würde so das man auch das neue Desing learn.microsoft.com/en-us/wind…riences/design-principles von Windows nutzen kann. Ja, über XAML Islands kann man das jetzt schon und da gibt es wohl auch Nuget-Pakete.
    Mfg -Franky-
    Ja gibt Nuget Packete, das hier z.B. Wird sicher auch was für VB geben, evtl. kann man das auch mit VB gebrauchen, habs nie probiert.

    nuget.org/packages/Microsoft.Windows.CsWinRT/

    Aber WinRT direkt in NET wirds nicht geben, NET ist ja eher Modular gehalten. Man braucht halt auch nicht immer alles, da finde ich optionale Packete durchaus sinnvoll. WinRT brauchte ich bisher nur im Zusammenhang mit BLE(BluetoothLowEnergy).
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    DTF schrieb:

    Ja gibt Nuget Packete, das hier z.B. Wird sicher auch was für VB geben

    Ich bin ja kein Freund von Nuget-Paketen. Da ist mir alles zu gekapselt und zu undurchsichtig. Ich bin ja eher der Typ: Nutze die COM-Interfaces direkt, da hast alles selbt in der Hand. ;)
    Mfg -Franky-
    Ja was COM angeht, da hab ich nun auch Blut geleckt. Schwer zu verstehen war das nicht, aber durchaus aufwändiger umzusetzen, dauert schon ein wenig die ganzen Interfaces, Structs und Enums zu tippen. Jedenfalls etwas bei dem ich auch am Ball bleiben werde. Ich denke wird in Richtung Videowiedergabe via COM gehen, das WPF-MediaElement läuft ja nicht so ganz flüssig bei Videos mit hoher Auflösung. Das wird sicher spannend werden. Hab dein Beispiel dazu hier im Forum schon gesehen, werde mal schauen welche Interfaces du verwendest hast und schauen was man damit alles so machen kann. Bin jedensfalls sehr optimistisch das zu packen. Bin schon gespannt wie lange das dauern wird, die Dialoge wollte ich auch "mal eben schnell" machen, hab fast eine Woche gebraucht :D und noch immer fehlen Kommentare(Naja, hab auch wirklich alle SystemErrorCodes(sogar dokumentiert) und HRESULT selbst implementiert, wird später in meine "NativeMethods"-Dll reinfliessen). Naja, COM in NET war halt Neuland für mich.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    DTF schrieb:

    Ja was COM angeht, da hab ich nun auch Blut geleckt. Schwer zu verstehen war das nicht, aber durchaus aufwändiger umzusetzen, dauert schon ein wenig die ganzen Interfaces, Structs und Enums zu tippen.

    Ich kopiere mir den größten Teil direkt aus den C++ Headerdateien heraus und mit ein bissel Search&Replace ist das meiste dann schon erledigt. Aber ja, es ist ein bissel arbeit die sich am Ende lohnt.

    DTF schrieb:

    Ich denke wird in Richtung Videowiedergabe via COM gehen, das WPF-MediaElement läuft ja nicht so ganz flüssig bei Videos mit hoher Auflösung.

    Hmm, das WPF und UWP-Media(Player)Element müsste im Hintergrund die MediaFoundation nutzen (nicht nachgeschaut). Müsste man mal testen ob die direkte Verwendung der MediaFoundation bessere Ergebnisse liefert. Du kannst Dir ja im Vergleich zum WPF-MediaElement auch mal das UWP-MediaPlayerElement per XAML Islands in VB.NET anschauen -> Projekt in Winui 3 übertragen -> XamlIslands.zip
    Mfg -Franky-
    ich hab mal mit wpf-Mitteln einen FolderBrowser-Dialog selbst-gebastelt.
    liegt inne wpf-Tutorials.

    (Antwort zu unten: ja, komisch, dasses den ihn immer noch nicht gibt als Standard-Dialog. Ich hab mein FolderBrowserDialog garnet so ernst genommen - das war nur ein Aufhänger für die Tutorial-Inhalte. Ich ging davon aus, dass MS da demnächst nachlegen wird - haben sie aber wohl nicht.)

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

    ErfinderDesRades schrieb:

    ich hab mal mit wpf-Mitteln einen FolderBrowser-Dialog selbst-gebastelt
    ;) Auch eine Option. Wobei sich mir die Frage stellt, warum ist das nicht in WPF direkt vorhanden wenn Windows dazu entsprechende COM-Interfaces (wohlgemerkt seit WinVista), von mir aus auch den alten Dialog per API SHBrowseForFolder, bereitstellt? Warum muss ich mir einen Standard-Dialog selbst zusammenbauen? *Kopfkratz* :S
    Mfg -Franky-

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

    Ja, das ist wirklich verwunderlich. Genauso verwunderlich finde ich auch, das beim Win32.OpenFileDialog die Options Readonly sind und die Klasse auch noch versiegelt, da dort auch IFileDialog zum Einsatz kommt, hätte man wenigsten die Option gehabt FILEOPENDIALOGOPTIONS::FOS_PICKFOLDERS einzustellen, schon hätte man ja einen FolderBrowserDialog gehabt. Einen komplett eigenen Dialog hab ich auch in Betracht gezogen, aber hab mich für IFileDialog entschieden.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „DTF“ ()

    DTF schrieb:

    Genauso verwunderlich finde ich auch, das beim Win32.OpenFileDialog die Options Readonly sind

    Man muss wohl den "dummen" Programmierer vor irgendwas schützen, glaub. Vor was auch immer. Ich habe mich auch schon diverses mal gewundert warum etwas in NET nicht funktioniert wie ich es normalerweise kenne oder es fehlt einfach bzw. wenn vorhanden, nur 90% vorhanden sind. Die restlichen 10% werden ignoriert, seit Jahren und da fängt das gefrickel an, das man eigentlich in NET nicht haben sollte.
    Mfg -Franky-
    Evtl. hatten die Angst das Default-Flags entfernt und eine FileNotFound-Exception geworfen wird, weil z.B. FOS_FILEMUSTEXIST nicht mehr gesetzt ist. Ist ja beim IFileOpenDialog default gesetzt. Oder das welche vorher nicht wie empfohlen erst mit GetOptions die Flags holen und so wichtige defaults verloren gehen.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Ich hab mir eben mal das Beispiel vom ErfinderDesRades angeschaut, ja so kann man das durchaus auch machen. Aber die Vorteile der IFileDialog-Dialoge finde ich es durchaus wert zu nutzen. Man hat sofort evtl. RecentOrdner, "Places", so wie der User es sich prepariert hat. Auch das der eingestellte Windows-Style angewendet wird finde ich sehr gut. Wobei das mit dem Style liesse sich in WPF auch leicht selbt machen. Jetzt muss ich erstmal Doku lesen(und die Posts von -Franky- zu dem Thema), solche speziellen Devices wie Tablets/Handys will ich auch drauf direkt zugreifen können, sowohl interner Speicher wie auch die SD-Karte.


    Haha, wo hatte ich denn da meinen Kopf, geht doch schon, nur wegen dem SetDefaultFolder muss ich dann noch gucken.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    Da hast mit den Möglichkeiten, die die neuen Dialoge bieten, Recht. Auch das diese leicht über IFileDialogCustomize anpassbar sind hat seinen Charme. PortableDevices ist ein anderes Thema. Da kannst Dich entweder komplett mit den WPD-Interfaces beschäftigen oder nur mit dem WPD-DeviceManager und den Rest mit Shell-Interfaces wie IFileDialog, IFileOperation und den üblichen Verdächtigen wie IShellItem(Array) usw. Ich würde letzteres Verwenden da das einfacher ist.

    Edit: SetDefaultFolder bekommt ein IShellItem. Der gilt aber nur beim allerersten Aufruf des Dialoges. Bei weiteren Aufrufen ist es dann automatisch der zuletzt ausgewählte Ordner. Das kannst Du aber unterbinden indem Du vorhwr ClearClientData aufrufst. Damit ist wieder alles so, wie beim allerersten Aufruf des Dialoges.
    Mfg -Franky-

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