繁体   English   中英

根据事件更改托盘图标

[英]change tray icon based on event

我有将在托盘(任务栏)中启动进程的代码。 右键单击托盘图标后,它将显示菜单。 单击第一个菜单项 winform 窗口启动后。 此 winform 显示有关记事本进程的状态。 我的目标是根据记事本的状态更改托盘图标(如果记事本正在运行,则显示online.ico否则显示offline.ico )。 如果我理解正确,那么每次打开/关闭 winform 窗口时我的代码都会启动/停止System.Windows.Forms.Timer ,我不确定这是否是最好的方法。 我的猜测是我需要以某种方式在OnMenuItem1ClickEventFn “外部”启动计时器,以便它可以以某种方式重新加载*.ico文件。 以下脚本深受网站的启发:

Add-Type -AssemblyName System.Windows.Forms    
Add-Type -AssemblyName System.Drawing

function Test-Notepad {
    [bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}


function OnMenuItem1ClickEventFn () {
    # Build Label object
    $Label = New-Object System.Windows.Forms.Label
        $Label.Name = "labelName"
        $Label.AutoSize = $True

    # Set and start timer
    $timer = New-Object System.Windows.Forms.Timer
    $timer.Interval = 1000
    $timer.Add_Tick({
        if ($Label){
            $Label.Text = if (Test-Notepad) { "Notepad is running" } else { "Notepad is NOT running" }
        }
    })
    $timer.Start()


    # 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.Add_Closing({ $timer.Dispose() })  # Dispose() also stops the timer.
        $Form.Controls.Add($Label)               # Add label to form
        $form.ShowDialog()| Out-Null             # Show the Form
}


function OnMenuItem4ClickEventFn () {
    $Main_Tool_Icon.Visible = $false
    $window.Close()
    Stop-Process $pid
}


function create_taskbar_menu{
    # Create menu items
    $Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
    $Main_Tool_Icon.Text = "Icon Text"
    $Main_Tool_Icon.Icon = $icon
    $Main_Tool_Icon.Visible = $true

    $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
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
# $icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")


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
[void][System.Windows.Forms.Application]::Run($appContext)

编辑:基于@BACON 答案的工作解决方案

# 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_Tool_Icon.Icon = $icon

...并定义要使用的图标...

# Choose an icon to display in the systray
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
# $icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")

...并定期测试Notepad是否正在运行并做出适当的响应...

$timer.Add_Tick({
    if ($Label){
        $Label.Text = if (Test-Notepad) { "Notepad is running" } else { "Notepad is NOT running" }
    }
})

你只需要将它们与一些额外的调整结合起来......

  • 使用描述性名称将每个图标存储在自己的变量中,以便在它们之间轻松切换。
  • $Main_Tool_Icon需要在create_taskbar_menu范围之外定义,以便它可以在OnMenuItem1ClickEventFn内部访问。 我将创建和初始化移动到调用create_taskbar_menu之前,但您也可以在create_taskbar_menu或其他一些函数内部对其进行初始化。

最终看起来像这样......

# 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

create_taskbar_menu

...和这个...

$timer.Add_Tick({
    if ($Label){
        # Change the text and icon with one test
        $Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
            "Notepad is running", $onlineIcon
        } else {
            "Notepad is NOT running", $offlineIcon
        }
    }
})

您将看到在初始化$Main_Tool_Icon和引发Tick事件时根据Test-Notepad的结果选择了一个图标。

至于在$Form关闭时处理$timer ......

$Form.Add_Closing({ $timer.Dispose() })

......这几乎是一个合适的地方,但是......

  • Closing事件本质Closing是在表单被请求关闭时引发的; 它为取消该请求提供了机会。 Closed事件更合适,因为它是在表单实际关闭时引发的。
  • 文档指出ClosingClosed事件都已过时。 请改用FormClosed事件

此外,在OnMenuItem4ClickEventFn ,即使未定义$window ,您也正在调用$window.Close() 我想你的意思是$Form.Close() 我认为更简洁的另一种方法是单击“ Exit菜单项仅退出 Windows 窗体应用程序循环......

function OnMenuItem4ClickEventFn () {
    $Main_Tool_Icon.Visible = $false

    [System.Windows.Forms.Application]::Exit()
}

...然后您可以将清理/拆卸代码放在脚本末尾的finally块中...

try
{
    # This call returns when [System.Windows.Forms.Application]::Exit() is called
    [System.Windows.Forms.Application]::Run($appContext)
}
finally
{
    # $timer would also have to be defined at the script scope
    # (outside of OnMenuItem1ClickEventFn) for this to work
    $timer.Dispose()

    # $Form, $Label, $Main_Tool_Icon, $onlineIcon, etc. would all be candidates for disposal...

    # Exit the entire PowerShell process
    Stop-Process $pid
}

以下是包含上述更改并适用于我的完整代码...

Add-Type -AssemblyName System.Windows.Forms    
Add-Type -AssemblyName System.Drawing

function Test-Notepad {
    [bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}


function OnMenuItem1ClickEventFn () {
    $timer.Start()

    # 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.Add_Closing({ $timer.Dispose() })  # Dispose() also stops the timer.
        $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
        }
    }
})

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, $form, $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
}

嗨,我知道在这里提出更多问题是违反规则的,因此我提出了与此相关的后续问题。 也许知识渊博的兰斯先生也为我解答了..谢谢

我的问题链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM