形式成为焦点

Form takes focus

Add-Type -AssemblyName System.Windows.Forms
$Form = [System.Windows.Forms.Form]::new()
$Form.TopMost = $true
$Form.ShowDialog()

如果我 运行 来自 powershell.exe 的代码,表单不会获得焦点。但是如果我 运行 来自 ISE 的这段代码,焦点就会转移到表单上。为什么会发生这种情况,如何解决?我希望表单不会像 powershell.exe 那样将焦点移开。

UPD
可能 this page 可以在这种情况下提供帮助...

使用 .ShowDialog() 方法调用形式 modally,这意味着您的 PowerShell 脚本的执行被 阻止(无响应) 直到表单 closed.

因此,您必须:

  • 使用.Show()方法显示形式非模态,确保您的PowerShell脚本继续执行.

    • 反过来,这需要您进入一个循环,在其中定期[System.Windows.Forms.Application]::DoEvents()调用,以确保表单保持响应。
  • 为了确保调用 .Show() 时窗体不接收焦点,您必须 子类 Forms class so as to override the ShowWithoutActivation property,如您所见。

    • 反过来,这需要通过 Add-Type.
    • 使用临时编译的 C# 代码实现子类
  • 警告如果您还想为表单设置 .TopMost = $true,以便始终在其他 windows 之上显示表格,需要 解决方法 以在各种主机环境中可靠运行 - 请参阅 底部.

综合起来:

  • 注意:启动脚本后按 Ctrl-C 将终止脚本并关闭表单。这有效的事实证明调用者的 window 保持了焦点。
# Derive a custom form class from System.Windows.Forms.Form
# that doesn't activate itself when loaded.
Add-Type -ReferencedAssemblies System.Windows.Forms, System.ComponentModel.Primitives @'
  public class MyForm: System.Windows.Forms.Form {
    protected override bool ShowWithoutActivation { get { return true; } }
  }
'@ -WarningAction Ignore

# Create an instance of the custom form class.
$form = [MyForm]::new()

# Show the form *non-modally*, with .Show() rather than
# .ShowDialog(), which is the prerequisite for not blocking this script.
$form.Show()

# Perform operations while the form is being shown.
try {
  do {

    # Process form events.
    [System.Windows.Forms.Application]::DoEvents()

    # Perform operations while the form is being displayed.
    Start-Sleep -Milliseconds 200
    Write-Host . -NoNewline

  } while ($form.Visible)
} finally {
  # Make sure that the form gets closed and disposed of.
  $form.Dispose()
}

对于反向用例,即如果您想确保表单确实获得焦点 - 默认情况下不会发生一致 - 使用以下内容:

在调用 $Form.ShowDialog() 之前,为 Load event 添加处理程序以确保表单在加载后接收焦点:

Add-Type -AssemblyName System.Windows.Forms

$form = [System.Windows.Forms.Form]::new()

# Ensure that the form receives the focus on loading.
# (Situationally, especially when run shortly after session startup, 
# the form may otherwise end up without the focus.)
$form.add_Load({
  $this.Activate()
})

$form.ShowDialog()

解决方法使表单最顶部

出于我不知道的原因,将表单的 .TopMost 属性 设置为 $true 可能会在(过时的)ISE 中间歇性地安静地发生故障,在Visual Studio 代码(ISE 后继者)以及 Windows 终端。

以下应该解决这些问题。请注意,window 可能会在调用方 window 重新激活之前非常短暂地激活,但在实践中应该不会引起注意:

Add-Type -AssemblyName System.Windows.Forms

# Create a helper type for activating a window by hWnd (window handle)
Add-Type -Namespace Util -Name WinApi -MemberDefinition @'
  [DllImport("user32.dll")]
  public static extern bool SetForegroundWindow(IntPtr hWnd);
  [DllImport("user32.dll")]
  public static extern IntPtr GetForegroundWindow();
'@

# Create an instance of the custom form class.
$form = [System.Windows.Forms.Form]::new()

# Get the caller's main window handle, to use it for reactivation later.
$thisHWnd = (Get-Process -Id $pid).MainWindowHandle

# Show the form *non-modally*, with .Show() rather than
# .ShowDialog(), which is the prerequisite for not blocking this script.
# Note: This *typically activates* the form (gives it the focus), though not consistently.
$form.Show()

# Perform operations while the form is being shown.
try {

  # Set the workaround flags.
  $makeTopMost = $true;  $reactivateMe = $true
  do {

    # Process form events.
    [System.Windows.Forms.Application]::DoEvents()

    # Apply workarounds and reset the flags.
    if ($reactivateMe)   { $null =[Util.WinApi]::SetForegroundWindow($thisHWnd); $reactivateMe = $false }
    if ($makeTopMost) { $form.TopMost = $true; $makeTopMost = $false }

    # Perform operations while the form is being displayed.
    Start-Sleep -Milliseconds 200
    Write-Host ([Util.WinApi]::GetForegroundWindow() -eq $thisHWnd) -NoNewline

  } while ($form.Visible)

} finally {
  # Make sure that the form gets closed and disposed of.
  $form.Dispose()
}

不确定您所说的 “表单没有获得焦点” 是什么意思,但我猜您希望它成为顶级 window。

那么,除了$Form.TopMost = $true,还要设置TopLevel 属性:

$form.TopLevel = $true
  • 顶级 表单是 window 没有父表单,或者其父表单是桌面 window。顶级 windows 通常用作应用程序中的主窗体。
  • topmost 表单是一种与所有其他(非最顶层)表单重叠的表单,即使它不是活动表单或前景表单。最顶部的表单始终显示在桌面上 windows z 顺序中的最高点。您可以使用此 属性 创建一个始终显示在您的应用程序中的表单,例如查找和替换工具 window.
  • 一个active表单意味着该表单有焦点。