Контекстное меню PowerShell NotifyIcon - PullRequest
0 голосов
/ 12 февраля 2019

У меня работает следующий NotifyIcon, использующий PowerShell:

NotifyIcon

Это контекстное меню, открываемое при щелчке правой кнопкой мыши по значку, оно просто показывает выход вмомент:

NotifyIcon right clicked

Я хотел бы знать, как я могу добавить два обработчика событий:

  1. Запускать функцию, когдаесть событие левого щелчка на значке
  2. Добавить еще один параметр в меню правого щелчка, которое также запускает функцию

Я искал в Интернете более часа, и яиспробовал около 20 различных вариантов, используя старый код с 5 или 6 разных веб-сайтов (все демонстрируют совершенно разные примеры).Я получил только головную боль.Кто-нибудь может предложить какое-либо руководство / направление?

$ProgramDataPath = "$ENV:ProgramData\test"
$ProgramFilesPath = "${env:ProgramFiles(x86)}\test"

[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")

$STForm = New-Object System.Windows.Forms.form
$NotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$ContextMenu = New-Object System.Windows.Forms.ContextMenu
$MenuItem = New-Object System.Windows.Forms.MenuItem
$Timer = New-Object System.Windows.Forms.Timer
$HealthyIcon = New-Object System.Drawing.Icon("$ProgramFilesPath\icons\healthy.ico")
$UnhealthyIcon = New-Object System.Drawing.Icon("$ProgramFilesPath\icons\unhealthy.ico")

$STForm.ShowInTaskbar = $false
$STForm.WindowState = "minimized"

$NotifyIcon.Icon = $HealthyIcon
$NotifyIcon.ContextMenu = $ContextMenu
$NotifyIcon.ContextMenu.MenuItems.AddRange($MenuItem)
$NotifyIcon.Visible = $True

# We need to avoid using Start-Sleep as this freezes the GUI. Instead, we'll utilitse the .NET forms timer, it repeats a function at a set interval.
$Timer.Interval = 300000 # (5 min)
$Timer.add_Tick({ Load-Config })
$Timer.start()

# This will appear as a right click option on the system tray icon
$MenuItem.Index = 0
$MenuItem.Text = "Exit"
$MenuItem.add_Click({
        $Timer.Stop()
        $NotifyIcon.Visible = $False
        $STForm.close()
    })

function Load-Config
{
    #Get-Content some Data from a file here
    if ($warn)
    {
        $NotifyIcon.Icon = $UnhealthyIcon
        $NotifyIcon.ShowBalloonTip(30000, "Attention!", "Some data from a file here...", [system.windows.forms.ToolTipIcon]"Warning")
        Remove-Variable warn
    }
    else
    {
        $NotifyIcon.Icon = $HealthyIcon
    }
}

Load-Config
[void][System.Windows.Forms.Application]::Run($STForm)

1 Ответ

0 голосов
/ 12 февраля 2019

Давайте поговорим о том, что вам действительно нужно.Похоже, у вас есть много ненужных частей, таких как таймер и так далее.Все, что вам нужно, это пространство пробега.Открытая форма будет держать пространство выполнения открытым без использования этого таймера.Убедитесь, что $Form.ShowDialog() - последний запуск.

Итак, давайте перейдем к всплывающему окну NotifyIcon.Метод, который делает это всплывающее окно, является частным, что означает, что нам нужно будет достичь его путем размышлений.Нам также нужно установить событие для значка уведомлений для запуска на MouseDown, а также нажать кнопку $_.button

Убедитесь, что вы установили $NotifyIcon.Icon для значка или же значок уведомленияне будет отображаться.

Рабочий сценарий

Add-Type -AssemblyName System.Windows.Forms

$form = New-Object System.Windows.Forms.Form
$form.ShowInTaskbar = $true
$form.WindowState = [System.Windows.WindowState]::Normal

$MenuItemLeft = New-Object System.Windows.Forms.MenuItem
$MenuItemLeft.Text = "Left Exit"
$MenuItemLeft.add_Click({
   $NotifyIcon.Visible = $False
   $form.Close()
   $NotifyIcon.Dispose()
})
$ContextMenuLeft = New-Object System.Windows.Forms.ContextMenu
$ContextMenuLeft.MenuItems.Add($MenuItemLeft)


$MenuItemRight = New-Object System.Windows.Forms.MenuItem
$MenuItemRight.Text = "Right Exit"
$MenuItemRight.add_Click({
   $NotifyIcon.Visible = $False
   $form.Close()
   $NotifyIcon.Dispose()
})
$ContextMenuRight = New-Object System.Windows.Forms.ContextMenu
$ContextMenuRight.MenuItems.Add($MenuItemRight)

$NotifyIcon= New-Object System.Windows.Forms.NotifyIcon
$NotifyIcon.Icon =  "C:\Test\Test.ico"
$NotifyIcon.ContextMenu = $ContextMenuRight
$NotifyIcon.add_MouseDown({
    if ($_.Button -eq [System.Windows.Forms.MouseButtons]::left ) {
        $NotifyIcon.contextMenu = $ContextMenuLeft
    }else{
        $NotifyIcon.contextMenu = $ContextMenuRight           
    }
    $NotifyIcon.GetType().GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic).Invoke($NotifyIcon,$null)
})
$NotifyIcon.Visible = $True

$form.ShowDialog()
$NotifyIcon.Dispose()

Я перечитал ваш пост, так что плохо предоставил вам самые важные части

Чтобы Powershell мог запустить ваши команды, должно быть пробелактивный.Runspace принимает команды powershell и превращает их в реальные действия.

Поскольку вы использовали для этого PowerShell, действия notifyicons зависят от пространства выполнения для интерпретации этих действий.

NotifyIcon в основном простозначок в углу, который может вызвать всплывающее уведомление или контекстное меню.

Поэтому, когда вы посмотрите, вы увидите $NotifyIcon.ContextMenu, это свойство, которое содержит объект ContextMenu .Объект контекстного меню содержит пунктов меню .

Поэтому просто добавьте MenuItems в объект ContextMenu, а затем добавьте этот объект ContextMenu в $ NotifyIcon.ContextMenu.Теперь вы можете изменить и добавить все элементы, которые вам нравятся.

Поскольку powershell ожидает закрытия формы, прежде чем перейти к следующей строке кода, $Form.ShowDialog() будет поддерживать пространство выполнения до выхода из формы.

Давайте посмотрим на этот неприятный беспорядок: $NotifyIcon.GetType().GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic).Invoke($NotifyIcon,$null)

Это называется отражением.Это позволяет вам взаимодействовать с классом.Проще говоря.Метод ShowContextMenu является закрытым и не может быть запущен обычным образом из-за пределов внутренней работы класса.Используя отражение, вы можете вызывать его в любом случае.

Итак, давайте разберем его немного подробнее, поскольку это действительно то, о чем вы спрашивали.

GetType () даст вам, что это за объект.Если бы я сделал "HEY".gettype(), я бы сказал, что этот объект был строкой.В этом случае $NotifyIcon.GetType() говорит мне, что это NotifyIcon.Что происходит, это возвращает меня к классу типов.

В этом мы видим GetMethod("ShowContextMenu"), но позвольте мне копнуть немного глубже ... Как мы узнали, что существует метод с именем ShowContextMenu .Что мы можем сделать, так это просмотреть все члены этого класса NotifyIcon, используя GetMembers().Теперь GetMembers() на самом деле просто поиск ... по умолчанию ищет только общедоступных участников, поэтому нам нужно искать всех участников.Параметры для поиска находятся в перечислении [System.Reflection.BindingFlags] и некоторых Битовая математика.

$BitWise
[System.Reflection.BindingFlags].GetEnumNames() | %{
    $BitWise = $BitWise -bor [System.Reflection.BindingFlags]$_ 
} | out-null

$NotifyIcon.GetType().GetMembers($BitWise) | ?{$_.Name -like "*Context*"} | select Name, MemberType

Это говорит найти все элементы, которые содержат слово Context в его имени и отобразить его полное имя и что это за тип.В ответ мы получаем

Name                 MemberType
----                 ----------
set_ContextMenu          Method
get_ContextMenu          Method
get_ContextMenuStrip     Method
set_ContextMenuStrip     Method
ShowContextMenu          Method
ContextMenu            Property
ContextMenuStrip       Property
contextMenu               Field
contextMenuStrip          Field

Мы можем видеть ShowContextMenu, и мы также можем видеть его метод

Так что теперь нам нужно получить этот метод напрямую.Здесь идет getMethod(), который является просто еще одним поиском, который возвращает 1 предмет вместо всех предметов.Так что

GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)

Get Method ShowContextMenu это будет Private, поэтому NonPublic и должен быть создан экземпляр класса, прежде чем он сможет работать таким образом. Instance.

.Invoke($NotifyIcon,$null)

Затем мы вызываем метод, сообщая ему, у какого элемента управления есть метод, который мы хотим запустить, и передаем любые параметры, которые не являются $ null.

И вот как вы это делаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...