1

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

mklement0
  • 382,024
  • 64
  • 607
  • 775
aarbuzik
  • 15
  • 4
  • according to this >>> c# - How to make a window always stay on top in .Net? - Stack Overflow — https://stackoverflow.com/questions/683330/how-to-make-a-window-always-stay-on-top-in-net <<< there is a `Form.Topmost` property you can set. have you tried that yet? – Lee_Dailey Aug 02 '21 at 13:06

2 Answers2

1

You can try this alternative instead:

function Select-Folder {
    [CmdletBinding()]
    param (
        #  sets the descriptive text displayed above the tree view control in the dialog box
        [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
        [string]$Message = "Please select a directory.",

        # sets the (pre)selected path
        [Parameter(Mandatory=$false, Position=1)]
        [string]$InitialDirectory,

        # sets the root folder where the browsing starts from
        [Parameter(Mandatory=$false)]
        [System.Environment+SpecialFolder]$RootFolder = [System.Environment+SpecialFolder]::Desktop,

        # sets a value indicating whether the 'New Folder' button appears in the folder browser dialog box
        [switch]$ShowNewFolderButton
    )
    Add-Type -AssemblyName System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()

    $dialog = New-Object System.Windows.Forms.FolderBrowserDialog
    $dialog.Description         = $Message
    $dialog.SelectedPath        = $InitialDirectory
    $dialog.RootFolder          = $RootFolder
    $dialog.ShowNewFolderButton = $ShowNewFolderButton.IsPresent

    $selected = $null

    # force the dialog TopMost:
    # because the owning window will not be used after the dialog has been closed,
    # you can simply create a new form inside the method call.
    $result = $dialog.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true; TopLevel = $true}))
    if ($result -eq [Windows.Forms.DialogResult]::OK){
        $selected = $dialog.SelectedPath
    }
    # clear the FolderBrowserDialog from memory
    $dialog.Dispose()
    # return the selected folder
    $selected
} 

Select-Folder -Message 'Select the folder where the move scripts reside' -ShowNewFolderButton
Theo
  • 57,719
  • 8
  • 24
  • 41
0

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()
mklement0
  • 382,024
  • 64
  • 607
  • 775