.net - 形式成为焦点

标签 .net powershell winforms

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() 方法模态调用表单,这意味着 PowerShell 脚本的执行被阻止(无响应),直到表单已关闭。

因此,您必须:

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

    • 这又要求您进入一个循环,在其中定期调用[System.Windows.Forms.Application]::DoEvents() ,以确保表单保持响应。
  • 为了确保表单在调用 .Show() 时不会获得焦点,您必须子类 Forms class 以便覆盖ShowWithoutActivation property ,正如您所发现的。

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

把它们放在一起:

  • 注意:启动脚本后按 Ctrl-C 将终止脚本并关闭表单。事实上,这有效证明了调用者的窗口保留了焦点。
# 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 Code(ISE 后继者)和 Windows 终端中的 session 。

以下内容应该可以解决这些问题。请注意,在重新激活调用者窗口之前,该窗口可能会短暂激活,但这在实践中应该不会引起注意:

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()
}

关于.net - 形式成为焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69692940/

相关文章:

c# - Visual Studio 中的程序集引用问题

.net - WCF:处理程序 "svc-ISAPI-4.0_32bit"有一个坏模块 "IsapiModule"

.net - 验证签名的 Word 文档的正确方法是什么?

arrays - -match…和|之间的区别其中{$ _ -match…}

winforms - 类未注册(来自 HRESULT : 0x80040154 (REGDB_E_CLASSNOTREG)) 的异常

c# - 当前上下文中不存在名称 Membership

.net - 通过本地网络的 URL 调用子程序?

.net - 如何在客户端计算机上安装 Microsoft.VisualStudio.Shell.dll

c# - 是否有 LINQ 扩展或(一组合理/高效的 LINQ 扩展)确定集合是否至少有 'x' 个元素?

powershell - 将新行从命令行传递到 PowerShell