【问题标题】:FolderBrowserDialog bring to frontFolderBrowserDialog 放在前面
【发布时间】:2019-01-04 10:38:07
【问题描述】:

我有以下运行良好的 PowerShell 函数,但窗口在 PowerShell ISE 后面的后台打开。

# Shows folder browser dialog box and sets to variable
function Get-FolderName() {
    Add-Type -AssemblyName System.Windows.Forms
    $FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{
        SelectedPath = 'C:\Temp\'
        ShowNewFolderButton = $false
        Description = "Select Staging Folder."
    }
    # If cancel is clicked the script will exit
    if ($FolderBrowser.ShowDialog() -eq "Cancel") {break}
    $FolderBrowser.SelectedPath
} #end function Get-FolderName

我可以看到有一个 .TopMost 属性可以与 OpenFileDialog 类一起使用,但这似乎不会转移到 FolderBrowserDialog 类。

我错过了什么吗?

【问题讨论】:

    标签: winforms powershell folderbrowserdialog


    【解决方案1】:

    我刚刚找到了一种获取 PowerShell 的 IWin32Window 值的简单方法,因此表单可以是模态的。创建一个 System.Windows.Forms.NativeWindow 对象并将 PowerShell 的句柄分配给它。

    function Show-FolderBrowser 
    {       
        Param ( [Parameter(Mandatory=1)][string]$Title,
                [Parameter(Mandatory=0)][string]$DefaultPath = $(Split-Path $psISE.CurrentFile.FullPath),
                [Parameter(Mandatory=0)][switch]$ShowNewFolderButton)
    
        $DefaultPath = UNCPath2Mapped -path $DefaultPath; 
    
        $FolderBrowser = new-object System.Windows.Forms.folderbrowserdialog;
        $FolderBrowser.Description = $Title;
        $FolderBrowser.ShowNewFolderButton = $ShowNewFolderButton;
        $FolderBrowser.SelectedPath = $DefaultPath;
        $out = $null;
    
        $caller = [System.Windows.Forms.NativeWindow]::new()
        $caller.AssignHandle([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
    
        if (($FolderBrowser.ShowDialog($caller)) -eq [System.Windows.Forms.DialogResult]::OK.value__)
        {
            $out = $FolderBrowser.SelectedPath;
        }
    
        #Cleanup Disposabe Objects
        Get-Variable -ErrorAction SilentlyContinue -Scope 0  | Where-Object {($_.Value -is [System.IDisposable]) -and ($_.Name -notmatch "PS\s*")} | ForEach-Object {$_.Value.Dispose(); $_ | Clear-Variable -ErrorAction SilentlyContinue -PassThru | Remove-Variable -ErrorAction SilentlyContinue -Force;}
    
        return $out;
    }
    

    【讨论】:

      【解决方案2】:

      希望对你有帮助

      Add-Type -AssemblyName System.Windows.Forms
      $FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog
      $FolderBrowser.Description = 'Select the folder containing the data'
      $result = $FolderBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true }))
      if ($result -eq [Windows.Forms.DialogResult]::OK){
      $FolderBrowser.SelectedPath
      } else {
      exit
      }
      

      //编辑评论

      ShowDialog() 方法有 2 个变体(重载)。

      查看文档:http://msdn.microsoft.com/en-us/library/system.windows.forms.openfiledialog.showdialog%28v=vs.110%29.aspx

      在第二个变体中,您可以指定应该是对话之母的窗口。

      Topmost 应谨慎使用或根本不使用!如果多个窗口是最顶层的,那么哪个是最顶层的? ;-)) 首先尝试将您的窗口设置为母亲,然后 OpenfileDialog / SaveFileDialog 应该始终出现在您的窗口上方:

      $openFileDialog1.ShowDialog($form1)
      

      如果这还不够,请选择 Topmost。

      您的对话窗口继承了母亲的属性。如果你的母窗口在最顶层,那么对话框也在最顶层。

      这是一个将对话设置为 Topmost 的示例。

      然而,在这个例子中,使用了一个新的未绑定窗口,所以对话框是未绑定的。

      $openFileDialog1.ShowDialog((New - Object System.Windows.Forms.Form - Property @{TopMost = $true; TopLevel = $true}))
      

      【讨论】:

      • 好的,所以我确实在搜索中找到了这种方法,但它似乎并不总是有效,只是似乎阻止了 vscode 在控制台中接受密钥输入以进行凭据调用。希望在我提供的原始代码中将“TopMost = $true”添加到我的属性列表中,但不喜欢它并且不明白为什么它可以用于您的...?
      • @Jaapaap 不幸的是,您的编辑都是关于OpenFileDialog,而不是FolderBrowserDialog
      【解决方案3】:

      执行此操作的可靠方法是将一段 C# 代码添加到函数中。 使用该代码,您可以获得实现IWin32Window 接口的Windows 句柄。在ShowDialog 函数中使用该句柄将确保对话框显示在顶部。

      Function Get-FolderName {   
          # To ensure the dialog window shows in the foreground, you need to get a Window Handle from the owner process.
          # This handle must implement System.Windows.Forms.IWin32Window
          # Create a wrapper class that implements IWin32Window.
          # The IWin32Window interface contains only a single property that must be implemented to expose the underlying handle.
          $code = @"
      using System;
      using System.Windows.Forms;
      
      public class Win32Window : IWin32Window
      {
          public Win32Window(IntPtr handle)
          {
              Handle = handle;
          }
      
          public IntPtr Handle { get; private set; }
      }
      "@
      
          if (-not ([System.Management.Automation.PSTypeName]'Win32Window').Type) {
              Add-Type -TypeDefinition $code -ReferencedAssemblies System.Windows.Forms.dll -Language CSharp
          }
          # Get the window handle from the current process
          # $owner = New-Object Win32Window -ArgumentList ([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
          # Or write like this:
          $owner = [Win32Window]::new([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
      
          # Or use the the window handle from the desktop
          # $owner =  New-Object Win32Window -ArgumentList (Get-Process -Name explorer).MainWindowHandle
          # Or write like this:
          # $owner = [Win32Window]::new((Get-Process -Name explorer).MainWindowHandle)
      
          $FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{
              SelectedPath = 'C:\Temp\'
              ShowNewFolderButton = $false
              Description = "Select Staging Folder."
          }
          # set the return value only if a selection was made
          $result = $null
          If ($FolderBrowser.ShowDialog($owner) -eq "OK") {
              $result = $FolderBrowser.SelectedPath
          }
          # clear the dialog from memory
          $FolderBrowser.Dispose()
      
          return $result
      }
      
      Get-FolderName
      

      您也可以选择使用 Shell.Application 对象,如下所示:

      # Show an Open Folder Dialog and return the directory selected by the user.
      function Get-FolderName {
          [CmdletBinding()]
          param (
              [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
              [string]$Message = "Select a directory.",
      
              [string]$InitialDirectory = [System.Environment+SpecialFolder]::MyComputer,
      
              [switch]$ShowNewFolderButton
          )
      
          $browserForFolderOptions = 0x00000041                                  # BIF_RETURNONLYFSDIRS -bor BIF_NEWDIALOGSTYLE
          if (!$ShowNewFolderButton) { $browserForFolderOptions += 0x00000200 }  # BIF_NONEWFOLDERBUTTON
      
          $browser = New-Object -ComObject Shell.Application
          # To make the dialog topmost, you need to supply the Window handle of the current process
          [intPtr]$handle = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle
      
          # see: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773205(v=vs.85).aspx
          $folder = $browser.BrowseForFolder($handle, $Message, $browserForFolderOptions, $InitialDirectory)
      
          $result = $null
          if ($folder) { 
              $result = $folder.Self.Path 
          } 
      
          # Release and remove the used Com object from memory
          [System.Runtime.Interopservices.Marshal]::ReleaseComObject($browser) | Out-Null
          [System.GC]::Collect()
          [System.GC]::WaitForPendingFinalizers()
      
      
          return $result
      }
      
      $folder = Get-FolderName
      if ($folder) { Write-Host "You selected the directory: $folder" }
      else { "You did not select a directory." }
      

      【讨论】:

      • 嗨 Theo,非常感谢您提供此代码的帮助。由于截止日期,我不得不将此要求放在列表的最后,但我会在可能的时候重新考虑。对迟到的回复表示歉意,但非常感谢。
      猜你喜欢
      • 1970-01-01
      • 2011-03-28
      • 2018-05-23
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 2011-07-02
      相关资源
      最近更新 更多