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
.
- 这又需要使用临时编译的 C# 代码实现子类,通过
警告:如果您还想为表单设置
.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/