Add-Type -AssemblyName System.Windows.Forms
$Form = [System.Windows.Forms.Form]::new()
$Form.TopMost = $true
$Form.ShowDialog()
If I run this code from powershell.exe, the form does not take focus. But if I run this code from ISE, focus shifts to the form. Why this happens, and how to fix it? I want the form not to take focus away, as the powershell.exe does.
UPD
May be this page can help in this situation...
CodePudding user response:
Using the .ShowDialog()
method invokes the form modally, which means that execution of your PowerShell script is blocked (unresponsive) until the form is closed.
Therefore, you must:
Use the
.Show()
method to show the form non-modally, which ensures that your PowerShell script continues executing.- This, in turn, requires you to enter a loop in which you call
[System.Windows.Forms.Application]::DoEvents()
periodically so as to ensure that the form stays responsive.
- This, in turn, requires you to enter a loop in which you call
To also ensure that the form doesn't receive the focus when
.Show()
is called, you must subclass theForms
class so as to override theShowWithoutActivation
property, as you have discovered.- This, in turn, requires implementing the subclass using ad-hoc-compiled C# code, via
Add-Type
.
- This, in turn, requires implementing the subclass using ad-hoc-compiled C# code, via
To put it all together:
# 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()
}
For the inverse use case, i.e. if you want to ensure that the form does receive the focus - which by default doesn't happen consistently - use the following:
Before calling $Form.ShowDialog()
, add a handler for the Load
event that ensures that the form receives the focus once loaded:
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()
CodePudding user response:
Not sure what you mean with "form does not take focus", but I'm guessing you want it to become the top-level window.
In that case, in addition to $Form.TopMost = $true
, also set the TopLevel property:
$form.TopLevel = $true
- A top-level form is a window that has no parent form, or whose parent form is the desktop window. Top-level windows are typically used as the main form in an application.
- A topmost form is a form that overlaps all the other (non-topmost) forms even if it is not the active or foreground form. Topmost forms are always displayed at the highest point in the z-order of the windows on the desktop. You can use this property to create a form that is always displayed in your application, such as a Find and Replace tool window.
- An active form means that the form has the focus.