Search code examples
powershellwinformsfolderbrowserdialog

BrowseForFolders doesn't display on top


I'm writing PS Script and following block of code shows dialogbox below windows forms gui.

    $btn1 = New-Object Windows.Forms.Button
    $btn1.Text = "Wybierz folder projektowy"
    $btn1.Location = New-Object System.Drawing.Point(170,140)
    $btn1.Size = New-Object System.Drawing.Size(160,20) 
    $btn1.add_Click({
        function Select-Folder($message='Select a folder', $path = 0) {
            $object = New-Object -comObject Shell.Application
            $object.topmost=$true
            $folder = $object.BrowseForFolder(0, $message, 0, $path)  
            if ($folder -ne $null) {    
                $folder.self.Path
                }
            }
            
            $folderPath = Select-Folder 'Select the folder where the move scripts reside'
            
            If ($folderPath) {
                Set-Content -Path "C:\Projekty\logs\temp_path.txt" -Value $folderPath.ToString() -Encoding Unicode
                write-host $folderPath 
                get-content -Path "C:\Projekty\logs\temp_path.txt" 
            }
            Else { Write-Host 'I do not have a folder path' }
            
    })
    $form_acl.Controls.Add($btn1) 

Is there any way to make it display on top? Here's screenshot of a problem:

screenshot


Solution

  • Theo's helpful answer shows an alternative, WinForms-based way to invoke a folder-browsing dialog, via the System.Windows.Forms.FolderBrowserDialog class.

    However, it seems that all that is missing from your original approach is to pass your form's window handle (hWND, .Handle) as the first argument to the Shell.Application COM object's .BrowseForFolder() method, which makes it the dialog's owner window and therefore shows the dialog on top of it - even if the form itself has the .TopMost property set:

    $folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
      $form_acl.Handle,  # Pass your form's window handle to make it the owner window 
      $message,
      0,       
      $path)
    

    Here's a simplified, self-contained example (requires PSv5+, but can be adapted to earlier versions):

    using namespace System.Windows.Forms
    using namespace System.Drawing
    
    Add-Type -AssemblyName System.Windows.Forms
    
    # Create a sample topmost form with a single
    # button that invokes a folder-browsing dialog.
    ($form = [Form] @{
      Text = "Topmost Form"
      Size = [Size]::new(300, 100)
      TopMost = $true  # Make the form topmost.
      StartPosition = 'CenterScreen'
    }).Controls.AddRange(@(
      ($folderBrowseButton = [Button] @{
        Location = [Point]::new(70, 20)
        Size = [Size]::new(160,30)
        Text = 'Browse for Folder'
      })
    ))
    
    $folderBrowseButton.add_Click({
      $path = 'C:\'
      # IMPORTANT: Pass $form.Handle, the form's window handle (HWND) as the first argument, which
      #            makes the form the owning window, ensuring that the dialog is displayed on 
      #            top - even if the form itself is set to topmost.
      $folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
        $form.Handle, 
        'Pick a target folder:', 
        0,     # Options
        $path  # Starting directory path.
      )  
      if ($null -ne $folder) {    
        Write-Verbose -vb ('Folder picked: ' + $folder.self.Path)
      } else {
        Write-Verbose -vb 'Folder browsing canceled.' 
      }
    })
    
    $null = $form.ShowDialog()