Die Community zu .NET und Classic VB.
Menü

Icon in der Taskleiste

 von 

Übersicht 

Dieser Artikel zeigt verschiedene wie ein Symbol in die Taskleisete gesetzt werden kann.

Mit freundlichen Grüßen
Herfried K. Wagner,

Beschreibung  

Der SystemTray-Bereich befindet sich auf der Taskleiste ganz rechts, eingebettet in ein Fenster der Klasse Shell_TrayWnd, das von einer einfachen Client-Kante umgeben ist. Den eigentlichen Tray-Bereich bildet ein Fenster von der Klasse TrayNotifyWnd, in dem die SystemTray-Icons und im Normalfall auch die Uhr plaziert sind. Die SystemTray-Icons sind maussensitiv, d.h. es werden alle Ereignisse der Maus an das Fensterprotokoll des angegebenen Fensters weitergeleitet. Daher ist es auch recht einfach, mit Microsoft Visual Basic ein Programmicon dem SystemTray-Bereich hinzuzufügen und die Ereignisse auszuwerten.


Abbildung 1: SystemTray-Bereich

Das SystemTray-Icon hat eine festgelegte Grösse, es sollte sich um ein 16-Farben Icon [unter Win95/98] handeln, das nur ein Icon für die Grösse 16 x 16 Pixel enthält.

Implementierung  

Folgende Struktur wird verwendet, um die benötigten Daten über die Shell_NotifyIcon API-Funktion an das System zu übergeben:

Private Type NOTIFYICONDATA
  'Länge der Struktur.
  cbSize As Long

  'Handle des Fensters, das die Meldungen erhalten soll.
  hwnd As Long

  'Anwendungsbezogene ID des Icons.
  uID As Long

  'NotifyIcon Flags.
  uFlags As Long

  'Gewünschte Message.
  uCallbackMessage As Long

  'Handle des Icons, das im SystemTray angezeigt werden soll.
  hIcon As Long

  'ToolTipText des Icons.
  szTip As String * MAX_TOOLTIP
End Type

Listing 1

Damit diese Struktur eingesetzt werden kann, müssen auch noch die entsprechenden Konstanten für die Flags und die Konstante MAX_TOOLTIP [gibt die maximale Länge des ToolTipTextes an] definiert werden. Im Folgenden die Deklaration der Shell_NotifyIcon API-Funktion:

Private Declare Function Shell_NotifyIcon Lib _
        "shell32.dll" Alias "Shell_NotifyIconA" _
        (ByVal dwMessage As Long, lpData As _
        NOTIFYICONDATA) As Long

Listing 2

Es ist zu beachten, dass auch, nachdem das Icon bereits hinzugefügt wurde, eine oder mehrere Eigenschaften des SystemTray-Icons verändert werden können.

Settings

Folgender Beispielcode fügt das Icon des Programmfensters dem SystemTray hinzu. Unter den Flags wird angegeben, dass sowohl ein Icon als auch ein ToolTipText angegeben wurden. Unter uCallbackMessage wurde definiert, welche Meldung an das in hwnd angegebene Fenster weitergeleitet werden soll. In diesem Beispiel wurde dafür die WM_MOUSEMOVE-Message angegeben, da dadurch die Auswertung direkt im MouseMove-Ereignis des Formulars stattfinden kann. Wenn die Meldungen an ein anderes Fenster weitergeleitet werden sollen, so muss das Handle dieses Fensters unter hwnd angegeben werden und ggf. das entsprechende Fenster nach der WM_MOUSEMOVE-Message gesubclasst werden. Anschliessend wird das Icon über Aufruf von Shell_NotifyIcon mit der Struktur und dem NIM_ADD-Parameter hinzugefügt. Wenn das Icon bereits hinzugefügt wurde, aber Änderungen vorgenommen werden sollen, muss Shell_NofifyIcon erneut mit der [modifizierten] Struktur aufgerufen werden, wobei statt NIM_ADD die Konstante NIM_MODIFY eingesetzt werden muss [diese Konstante wird im hier beschriebenen Beispiel nicht verwendet].

With m_nfiIconData
  .hwnd = Me.hwnd
  .uID = Me.Icon
  .uFlags = NIF_ICON Or NIF_MESSAGE Or NIF_TIP
  .uCallbackMessage = WM_MOUSEMOVE
  .hIcon = Me.Icon.Handle
  .szTip = "System Tray Example" & vbNullChar
  .cbSize = Len(m_nfiIconData)
End With

'Add Icon to SystemTray.
Call Shell_NotifyIcon(NIM_ADD, m_nfiIconData)

Listing 3

Der Aufruf zum Entfernen des Icons erfolgt analog, nur dass in dwMessage die Konstante NIM_DELETE angegeben wird.

Funktionalität hinzufügen  

Nachdem das Icon erfolgreich hinzugefügt wurde, kann man die Ereignismeldungen in der MouseMove-Prozedur des Formulars (in diesem Beispiel) anfangen und auswerten. Dazu muss der x-Parameter durch Screen. TwipsPerPixelX dividiert werden und das Resultat mit den für uns interessanten Window-Messages verglichen werden. Dies kann man am Besten über eine Select-Anweisung erreichen. Anschliessend führt man dann die entsprechenden Aktionen aus. Auf diese Weise könnte z.B. bei Klick auf das Icon ein Formular angezeigt werden oder ein anderer Prozess gestartet werden. Allerdings können auf diese Weise nur wenige Aktionen mit dem SystemTray-Icon verbunden werden, nämlich bei Klick bzw. Doppelklick mit rechter und linker Maustaste. Besser ist es, ein PopUp-Menü anzuzeigen, das Einträge für die Funktionen enthält. Das Anzeigen des PopUp-Menüs findet meist beim Klick mit der rechten Maustaste statt, d.h. beim MouseUp der rechten Maustaste [Konvention des Kontextmenüs].

Am Einfachsten geht das, indem man mit dem Menü-Editor ein Menü erstellt, das dann als PopUp-Menü fungiert, man könnte das Menü aber auch aus einer Ressourcendatei laden bzw. mit den entsprechenden APIs zur Laufzeit erstellen. Auch die Anzeige des PopUp-Menüs über die PopUpMenu-Methode ist kein Problem - das Menü wird genau so angezeigt, wie man es von anderen Anwendungen kennt. Auch die Auswahl von einzelnen Befehlen im Menü funktioniert einwandfrei, solange sie über die Maus erfolgt, versucht man aber, die Zugriffstasten der Menüeinträge zu verwenden bzw. mit den Auf- und Ab-Tasten einen Eintrag zu selektieren, so wird man feststellen, dass das Menü nicht auf Tastatureingaben reagiert [nicht den "Fokus" hat].

Problem und Lösung  

Die VB-Programmierer waren den Programmierern, die mit C++ arbeiten deshalb immer neidisch, da es kein einfaches (und auch kompliziertes) Mittel gab, um das PopUp-Menü tastatursensitiv zu machen. Matt E. Hart schrieb zwar einmal ein Beispiel zur Lösung dieses Problems und publizierte es auf seiner Homepage, allerdings fuktionierte es nicht so wie beschrieben. Zahlreiche Versuche, ein Beispiel zu finden bzw. zu schreiben, bei dem dies funktionierte, scheiterten, selbst das Erstellen und Anzeigen des PopUp-Menüs über das Win32-API brachte nicht den gewünschten Effekt. Später fand ich dann ein Beispiel von Marcus Warm, aber auch dieses funktionierte nur bedingt: Das Menü reagierte erst ab dem 2. Anzeigen auf die Tastatur - Keine Chance, auch nicht über das API, um das Beispiel in allen Fällen zum Laufen zu bringen. In der IDE funktionierte es zwar einwandfrei, aber nicht als EXE...

Die hier präsentierte Lösung macht sich einen kleinen Trick zunutze, um auch auf Tastatureingaben zu reagieren. Es wird ein Formular (frmDummy) erstellt, das nicht in der Taskleiste angezeigt wird. Dieses Formular wird überhaupt nie angezeigt. Wenn nun das PopUp-Menü erscheinen soll, wird dieses Formular in den Vordergrund gesetzt (aber es beleibt unsichtbar) und anschliessend auf diesem Formular das PopUpMenü angezeigt:

Call SetForegroundWindow(frmDummy.hwnd)
frmDummy.PopupMenu mnuPopUp, , , , mnuPopUpShowForm

Listing 4

Zu meiner grossen Verwunderung brachten diese 2 Zeilen Code den grossen Durchbruch - unter Windows 98/Me/2000 funktionierte das Beispiel sowohl in der IDE als auch als kompilierte EXE.

Beispiel als Download [53100 Bytes]

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.