Add-Type -AssemblyName System.Windows.Forms
$Form = [System.Windows.Forms.Form]::new()
$Form.TopMost = $true
$Form.ShowDialog()
如果我从powershell.exe中运行这段代码,窗体不会集中。但是如果我从ISE运行这段代码,焦点就会转移到表单上。为什么会发生这种情况,如何解决?我希望表单不会像powershell.exe那样带走焦点。
乌利希期刊指南
也许这个页面可以帮到你…
使用.ShowDialog()
方法调用表单模式,这意味着你的PowerShell脚本的执行被阻塞(无响应),直到表单被关闭。
因此,你必须:
使用
.Show()
方法显示表单非模态,这确保您的PowerShell脚本继续执行.- 这反过来要求您进入循环,在该循环中定期调用
[System.Windows.Forms.Application]::DoEvents()
确保表单保持响应。
- 这反过来要求您进入循环,在该循环中定期调用
为了确保窗体在
.Show()
被调用时不接收焦点,您必须子类Forms
类以便重写ShowWithoutActivation
属性,如你所知。- 这反过来又需要通过
Add-Type
使用ad-hoc编译的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
事件添加一个处理程序,以确保表单在加载后接收焦点:
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 Terminal的会话中的第一次调用中悄悄地,间歇性地发生故障。
下面应该可以解决这些问题。请注意,在调用者窗口重新激活之前,窗口可能会短暂激活,但在实践中不应该引起注意:
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()
}
我不知道你说的"form does Not take focus">是什么意思,但我猜你想让它成为顶层窗口。
在这种情况下,除了$Form.TopMost = $true
,还要设置TopLevel属性:
$form.TopLevel = $true
- 顶级窗体是没有父窗体或其父窗体是桌面窗口的窗口。顶层窗口通常用作应用程序中的主窗体。
- 顶端的表单是与所有其他(非顶部)表单重叠的表单,即使它不是活动表单或前景表单。最顶层的窗体总是显示在桌面上窗口z轴顺序的最高点。您可以使用此属性创建一个始终显示在应用程序中的表单,例如查找和替换工具窗口。 <
- 活跃/strong>