【问题标题】:Form takes focus形式成为焦点
【发布时间】:2021-12-10 01:06:42
【问题描述】:
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 可以在这种情况下提供帮助...

【问题讨论】:

    标签: .net powershell winforms


    【解决方案1】:

    使用.ShowDialog() 方法调用表单模态,这意味着您的PowerShell脚本的执行被阻塞(无响应)直到表单关闭。

    因此,您必须:

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

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

      • 反过来,这需要通过 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 终端中。

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

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

    【讨论】:

      【解决方案2】:

      不确定您对“表单没有获得焦点”的含义,但我猜您希望它成为顶级窗口。

      这种情况下,除了$Form.TopMost = $true,还要设置TopLevel属性:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-12
        • 1970-01-01
        • 2018-06-28
        • 2012-04-29
        相关资源
        最近更新 更多