Winforms 根据事件更改托盘图标
我有代码,将启动托盘(任务栏)中的进程。右键单击托盘图标后,将显示菜单。单击第一个菜单项后,winform窗口启动。此winform显示记事本进程的状态。我的目标是根据记事本的状态更改托盘图标(如果记事本正在运行,则显示Winforms 根据事件更改托盘图标,winforms,powershell,Winforms,Powershell,我有代码,将启动托盘(任务栏)中的进程。右键单击托盘图标后,将显示菜单。单击第一个菜单项后,winform窗口启动。此winform显示记事本进程的状态。我的目标是根据记事本的状态更改托盘图标(如果记事本正在运行,则显示online.ico,否则显示offline.ico)。如果我理解正确,那么每次打开/关闭winform窗口时,我的代码都会启动/停止System.Windows.Forms.Timer,我不确定这是否是最好的方法。我猜我需要在OnMenuItem1ClickEventFn之外启
online.ico
,否则显示offline.ico
)。如果我理解正确,那么每次打开/关闭winform窗口时,我的代码都会启动/停止System.Windows.Forms.Timer
,我不确定这是否是最好的方法。我猜我需要在OnMenuItem1ClickEventFn
之外启动计时器,以便它能够以某种方式重新加载*.ico
文件。以下脚本深受该网站的启发:
编辑:基于@BACON-answer的工作解决方案
# Toggle following two lines
Set-StrictMode -Version Latest
# Set-StrictMode -Off
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function OnMenuItem1ClickEventFn () {
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
function create_taskbar_menu{
# Create menu items
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Menu Item 2"
$MenuItem3 = New-Object System.Windows.Forms.MenuItem
$MenuItem3.Text = "Menu Item 3"
$MenuItem4 = New-Object System.Windows.Forms.MenuItem
$MenuItem4.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem3)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem4)
$MenuItem4.add_Click({OnMenuItem4ClickEventFn})
$MenuItem1.add_Click({OnMenuItem1ClickEventFn})
}
$Current_Folder = split-path $MyInvocation.MyCommand.Path
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# [System.Reflection.Assembly]::LoadFrom("Current_Folder\assembly\MahApps.Metro.dll") | out-null
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Initialize the timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
$timer.Start()
create_taskbar_menu
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://docs.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
try
{
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
foreach ($component in $timer, $Main_Tool_Icon, $offlineIcon, $onlineIcon, $appContext)
{
# The following test returns $false if $component is
# $null, which is really what we're concerned about
if ($component -is [System.IDisposable])
{
$component.Dispose()
}
}
Stop-Process -Id $PID
}
您已经有了设置
NotifyIcon
使用的图标的代码
$Main\u Tool\u Icon.Icon=$Icon
…并定义要使用的图标
#选择要在systray中显示的图标
$icon=[System.Drawing.icon]::提取关联图标($Current\u Folder/icons/online.ico)
#记事本未运行时使用此图标
#$icon=[System.Drawing.icon]::提取关联图标($Current\u Folder/icons/offline.ico)
…并定期测试记事本是否正在运行并做出适当响应
$timer.Add\u勾选({
如果($标签){
$Label.Text=如果(测试记事本){“记事本正在运行”}否则{“记事本没有运行”}
}
})
你只需要把它们和一些额外的调整结合起来
- 用描述性名称将每个图标存储在自己的变量中,以便在它们之间轻松切换
需要在$Main\u Tool\u图标
的范围之外定义,以便可以在创建任务栏菜单
OnMenuItem1ClickEventFn的内部访问它。我将创建和初始化移动到调用
之前,但您也可以在创建任务栏菜单
或其他一些功能中对其进行初始化创建任务栏菜单
#选择要在systray中显示的图标
$onlineIcon=[System.Drawing.Icon]::ExtractAssociatedIcon($Current\u Folder/icons/online.ico”)
#记事本未运行时使用此图标
$offlineIcon=[System.Drawing.Icon]::ExtractAssociatedIcon(“$Current\u Folder/icons/offline.ico”)
$Main\u Tool\u Icon=新对象System.Windows.Forms.NotifyIcon
$Main\u Tool\u Icon.Text=“Icon Text”
$Main_Tool_Icon.Icon=if(测试记事本){$onlineIcon}else{$offlineIcon}
$Main\u Tool\u Icon.Visible=$true
创建任务栏菜单
…还有这个
$timer.Add\u勾选({
如果($标签){
#通过一次测试更改文本和图标
$Label.Text、$Main\u Tool\u Icon.Icon=if(测试记事本){
“记事本正在运行”,$Online图标
}否则{
“记事本未运行”,$offlineIcon
}
}
})
在初始化$Main\u Tool\u icon
和引发勾选事件时,您将看到根据测试记事本的结果选择了一个图标
当$Form
关闭时处理$timer
$Form.Add\u关闭({$timer.Dispose()})
…这几乎是一个合适的地方,但是
- 当要求关闭表单时,基本上会引发该问题;它提供了取消该请求的机会。更合适的方法是在表单实际关闭时引发
- 文档说明
关闭
和关闭
事件都已过时。改用新的
另外,在OnMenuItem4ClickEventFn
中,您正在调用$window.Close()
,即使未定义$window
;我想你的意思是$Form.Close()
。我认为稍微干净一点的另一种选择是,只需单击退出
菜单项
函数OnMenuItem4ClickEventFn(){
$Main\u Tool\u Icon.Visible=$false
[System.Windows.Forms.Application]::Exit()
}
…然后您可以将清理/拆卸代码放入脚本末尾的finally
块中
试试看
{
#调用[System.Windows.Forms.Application]::Exit()时返回此调用
[System.Windows.Forms.Application]::运行($appContext)
}
最后
{
#$timer还必须在脚本范围内定义
#(在OnMenuItem1ClickEventFn之外)以使其工作
$timer.Dispose()
#$Form、$Label、$Main\u Tool\u Icon、$onlineIcon等都将是处理的候选项。。。
#退出整个PowerShell进程
停止进程$pid
}
以下是包含上述更改的完整代码,适用于我
添加类型-AssemblyName System.Windows.Forms
添加类型-AssemblyName System.Drawing
功能测试记事本{
[bool](获取进程-名称“记事本”-ErrorAction SilentlyContinue)
}
函数OnMenuItem1ClickEventFn(){
$timer.Start()
#生成表单对象
$Form=新对象System.Windows.Forms.Form
$Form.Text=“我的表格”
$Form.Size=新对象系统.Drawing.Size(200200)
$Form.StartPosition=“中心屏幕”
$Form.Topmost=$True
$Form.Add_Closing({$timer.Dispose()})#Dispose()也会停止计时器。
$Form.Controls.Add($Label)#将标签添加到表单
$form.ShowDialog()| Out Null#显示表单
}
函数OnMenuItem4ClickEventFn(){
$Main\u Tool\u Icon.Visible=$false
[System.Windows.Forms.Application]::Exit()
}
功能创建\u任务栏\u菜单{
#创建菜单项
$MenuItem1=新对象System.Windows.Forms.MenuItem
$MenuItem1.Text=“菜单项1”
$MenuItem2=新对象System.Windows.Forms.MenuItem
# Toggle following two lines
Set-StrictMode -Version Latest
# Set-StrictMode -Off
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function OnMenuItem1ClickEventFn () {
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
function create_taskbar_menu{
# Create menu items
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Menu Item 2"
$MenuItem3 = New-Object System.Windows.Forms.MenuItem
$MenuItem3.Text = "Menu Item 3"
$MenuItem4 = New-Object System.Windows.Forms.MenuItem
$MenuItem4.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem3)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem4)
$MenuItem4.add_Click({OnMenuItem4ClickEventFn})
$MenuItem1.add_Click({OnMenuItem1ClickEventFn})
}
$Current_Folder = split-path $MyInvocation.MyCommand.Path
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# [System.Reflection.Assembly]::LoadFrom("Current_Folder\assembly\MahApps.Metro.dll") | out-null
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Initialize the timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
$timer.Start()
create_taskbar_menu
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://docs.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
try
{
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
foreach ($component in $timer, $Main_Tool_Icon, $offlineIcon, $onlineIcon, $appContext)
{
# The following test returns $false if $component is
# $null, which is really what we're concerned about
if ($component -is [System.IDisposable])
{
$component.Dispose()
}
}
Stop-Process -Id $PID
}