System tray icon

Tags:    visual-basic
Skrevet af Bruger #2165 @ 03.09.2004
I denne artikel vil jeg beskrive og forklare hvorfor og hvordan man placere et ikon nede ved siden af uret på proceslinien. For at opnå dette vil jeg bruge API kaldet Shell_NotifyIcon() som der naturligvis følger en forklaring med til!

Formålet med et sådan ikon


Det man opnår ved at placere sit ikon dernede finder du lettest ved at kigge på hvilke programmers ikoner du selv har liggende dernede! Du har måske et program til at indstille lydstyrken eller din firewall måske. Men fælles for alle programmerne er deres formål med at ligge dernede. Det er nemlig en diskret måde at gemme et program af vejen som man ikke bruger så tit og som man ikke åbner og lukker lige så ofte som en browser eller en medieafspiller.
Størstedelen af disse programmer er også dem som starter op sammen med computeren og ligger der lige til man lukker den igen. Hvor ofte får du måske brug for at åbne nogle af disse omtalte programmer i forhold til hvor ofte du åbner alle dine andre programmer?
Ud over at være diskret gemt af vejen har system tray ikoner har også den fordel at man slet ikke behøver at åbne disse programmer for at få adgang til deres funktioner. Man kan nemlig højreklikke på dem og få en lille menu poppet op hvor man kan vælge blandt et udvalg funktioner. Som et eksempel kan jeg nævne chat programmer hvor man kan vælge status så man lyn hurtigt kan fortælle andre at man er "Away" uden at man behøver at åbne programmets vindue!

En anden smart funktion ved system tray ikoner som chat programmer og firewalls benytter sig meget af er signaler. Når ens firewall opsnapper en virus eller et hacker angreb signalerer den til brugeren ved evt. at animere sit ikon så det blinker (Northon Internet Security). På den måde ved brugeren hvad der er sket uden at modtage en irriterende 'msgbox' som vi vb programmører nyder at bruge når vi skal signalere til brugeren.

System tray ikoner kan bruges til mange smarte formål. Fx hvis du vil lave et e-mail program ville det jo være smart hvis det, når dets vindue blev minimeret, kunne ligge sig til ro som et lille ikon. Her kunne det så tjekke mail hvert 10 minut og hvis brugeren havde modtaget nogen mails kunne det signalere om dette ved en animation eller simpelt ved bare at ændre ikon. Man kunne også sætte det til at animere hver gang den tjekkede mail for at minde brugeren om hvorfor hans internet forbindelse pludselig kørte langsommere.

Gennemgang af API kaldet


Lad os begynde med at kigge lidt på API kaldet...


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


Lad dig for resten ikke undre af _ tegnet i slutningen af første linie. Det er bare en måde i vb at knække koder over i flere dele så man ikke behøver at scrolle sidelæns. Her har jeg brugt det til det formål at jeg ikke ville have at koden blev delt op i underlig bider.

Som du kan se skal Shell_NotifyIcon bruge to parametre for at kunne udføres. Herunder vil jeg give en lille forklaring af dem hver så du vil forstå dem senere når vi begynder at bruge dem:

dwMessage - Dette parameter bestemmer hvad der skal ske. Her har du flere valgmuligheder eftersom det både er muligt at oprette, ændre og slette system tray ikoner.

&H0 - Opretter et system tray ikon.
&H1 - Ændre i et system tray ikon.
&H2 - Sletter et system tray ikon.

lpData - Dette parameter identificere ikonet og indeholder en masse informationer, der gør at du kan identificere præcis det system tray ikon der tilhører dit program. Det indeholder også selve ikonet og et ToolTip, som er den lille tekstboks der popper op når du holder musen hen over ikonet.
Når du som programmør skal holde styr på en sådan portion data opretter vi en 'Type'. Den virker på den måde at man kan ligge en masse variabler ind under en fælles variabel og altså holde ordentlig styr på dem.
Denne kode skal stå i et modul...
Fold kodeboks ind/udKode 

Inden vi kommer for godt igang med at forklare koden må jeg hellere lige huske at nævne at 'IconData' skal indsættes som parameteret 'lpData'!

Læg mærke til navnet på vores Type 'NotifyIconData' som er det samme som der står det skal være i API kaldet (lpData As NotifyIconData). Man kan ikke skrive direkte til en Type (se det som en skabelon) så vi opretter en pointer kaldet 'IconData' som indeholder de samme variabler, men som blot ikke er "fastlåst" så man altså kan ændre på variablerne inden i. Jeg tror hellere jeg må til at forklare de enkelte variabler i Type'en.

cbSize - Denne variabel er ikke specielt interessant da det bare skal indeholde størrelsen på vores IconData. Størrelsen findes ved at bruge kommandoen 'Len(IconData)'.

hWnd - Denne variabel er heller ikke interessant da den blot indeholder ID-nummeret til ikonet. Ikonets hWnd skal være lig med hWnd'en fra den form, i din applikation, du vil refererer til fra dit ikon. Som regel skal man bare skrive 'Me.hWnd' da det oftest er formen der kalder API'en som der også skal refereres til.

uID - Denne variabel bliver slet ikke brugt i vb! Derfor skal det sættes til ingenting som skrives 'vbNull'. Du kan dog også nøjes med at sætte den til '0' som jeg plejer.

uFlags - Helt simpelt er flags "en måde hvorpå du kan give et argument flere værdier" citat af HyBreeD. Værdien her er altid den samme gang kode, nemlig '&H1 Or &H2 Or &H4'.

uCallbackMessage - Dette er den variabel der bestemmer hvornår du vil have at en sub bliver kørt. Dette gør det lettere at håndtere system tray ikonet. Det smarteste er at sætte argumentet til når musen bevæger sig hen over. Mest fordi at du derved senere i den omtalte sub kan tjekke om der er sket andet (evt. venstre klik, højre klik, dobbelt klik osv.). Jeg vil senere gennemgå de enkelte ting man kan sætte uCallbackMessage til, men hvis du vil sætte den til MouseMove skal du sætte uCallbackMessage til '&H200'.

hIcon - Variablen styrer hvilket ikon der skal vises nede i proceslinien. Ofte vil man bruge formens ikon som tray ikon, dette fås ved at skrive 'Me.Icon'. Men dette er ikke altid optimalt og derfor kan man loade billeder fra harddisken og bruge dem. Dette gøres ved at skrive 'LoadPicture(FileName)' hvor du erstatter FileName med stien til ikonet. Og den sidste måde der også tit bliver brugt er hvor du loader ikonet fra en PictureBox. Dette ikon fås helt enkelt med koden 'picIcon.Picture', hvor du så erstatter 'picIcon' med din PictureBox.
Du skal bare altid huske at formatet på billedet, lige gyldigt hvilken metode du bruger, altid skal være af filtypen *.ico!

szTip - Her skal du skrive din ToolTip besked som brugen jo som sagt ser når han holder musen hen over ikonet. Hvis du kigger på hvordan vi definerer det ser du at det har en fast størrelse på 64 tegn (szTip As String * 64). Så du skal altså bare give den en string på max 63 tegn og slutte den af med en 'Chr$(0)'.

Hvordan man bruger det i praksis


Hvis du har i sinde at bruge system tray ikoner i din applikation anbefaler jeg at du har et solidt modul at forholde dig til. Der findes sikkert en masse andre moduler der ligner, men i min version har jeg satset på at gøre det overskueligt og brugbart, se selv efter:
Fold kodeboks ind/udKode 

De underlige værdier er pakket ind i konstanter med meget mere forståelige navne der gør arbejdet lidt lettere.
Nu har du faktisk det grundlæggende, men ved måske stadig ikke helt hvordan det skal bruges. Derfor har jeg lavet et eksempel som for resten også løser problemet med blinkene vinduer efter gendannelse - et problem som mange nybegyndere måske støder på... Men alt det senere...

Når formen loader sætter vi værdierne i IconData, så skal vi nemlig ikke tænke på dette senere:
Fold kodeboks ind/udKode 

Hvis du har læst grundigt og har kendskab til vb's kommando 'With', ved du allerede hvad der sker her. Den eneste forskel fra mine tidligere forklaringer er at jeg her har brugt konstanternes navne i stedet for værdierne.

Det næste man skal tage højde for er at der skal tjekkes hver gang formen ændre størrelse. Hvis formen pludselig bliver minimeret skal vi være klar til at oprette system tray ikonet:
Fold kodeboks ind/udKode 

I denne procedure har jeg en Static variabel kaldet LastWindowState. Den skal sørge for at hvis proceduren bliver kaldt igen, efter den er blevet minimeret, og stadig er minimeret (eller hvis der sker noget uventet) så skal den ikke oprette endnu et ikon. Og ligeså skal den heller ikke bruge ressourcer på at slette tray ikoner der ikke er der!
At variablen er Static betyder at den husker værdien til næste gang proceduren bliver kaldet og at den er af type Byte er kun fordi jeg mener man bør spare på ram-lageret de steder man kan, selvom det er ubetydelig lidt.

Jeg kalder Shell_NotifyIcon med IconData og et parameter bestemt af formålet med API kaldet.
'Me.Hide' bruges fordi at det ville være upraktisk både at have programmet minimeret normalt og som tray ikon. Så vi gemmer den normale minimering af vinduer væk med den nævnte kommando.

Nu da ikonet er oprettet skal det tages i brug:
Fold kodeboks ind/udKode 

At vi tidligere satte IconData.uCallbackMessage til konstanten Tray_MouseMove viser nu sin fulde effekt. Hver gang musen bevæger sig over system tray ikonet vil denne procedure blive kaldt hvori man finder en hemmelig værdi i X. Modsat normal opførsel bestemmer denne værdi brugerens handling med ikonet.

Som du kan se har jeg allerede indsat nogle kommandoer for at give dig et indtryk af de muligheder et system tray ikon giver. Hvis man dobbeltklikker på ikonet skal vinduet poppe op igen og system tray ikonet forsvinde, dette sørger funktionen ShowWindow() for.
Hvis man højreklikker på tray ikonet vil en lille popup menu dukke op. Denne menu laver du ligesom andre menuer:

mnuTrayMenu <-- visible = false
....mnuOpenWindow <-- visible = true

Denne oprettede menu vil være usynlig i et normalt vindue men når du beder om at få den usynlige mappe vist med Me.PopupMenu kommandoen, vil alle de synlige felter i menuen komme til syne!
Du koder til disse menuer på samme måde som almindelige:
Fold kodeboks ind/udKode 

Nu har jeg henvist til funktionen ShowWindow 2 gange allerede og jeg har stadig ikke vist jer den, men nu er tiden kommet til det:
Fold kodeboks ind/udKode 

Det er her nogle enkelte kunne finde på at bytte om på rækkefølgen af disse to kommandoer, hvilket ikke er en særlig brugbar løsning!

Til sidst har jeg lige et sidste tricks. Hvis man afslutter programmet i minimeret tilstand vil system tray ikonet stadig vises indtil musen føres henover det (hvert fald i winME og win98) fordi styresystemet ikke har registreret at system tray ikonets reference ikke længere eksisterer.
For at undgå dette går vi efter princippet at hellere slette det en gang for meget end en gang for lidt:
Fold kodeboks ind/udKode 


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (9)

User
Bruger #6130 @ 10.09.04 15:31
God artikkel men lidt svær at føgle hvis man ikke tænker så meget over det.
Men emnet havde jeg tænk over.
User
Bruger #1561 @ 13.12.04 13:59
Meget god artikel, god forklaring. noget som alle kan bruge viste godt hvordan man gjorde det i forvejen men her forklare du :) forsadt god arbejdslyst
User
Bruger #7706 @ 04.06.05 21:46
meget speciel forklaring :O :D
Jeg sad lige og manglede hjælp til at få en ventrilo server derned.. Googlede og fandt denne..
Kan dog slet ikke følge din forklaring så kunne godt bruge lidt ekstra hjælp :)
User
Bruger #7706 @ 05.06.05 17:43
ved ikke om det er for besværligt men jeg kunne godt bruge noget punkt fot punkt hjælp.
evt. over msn?
tak :)
User
Bruger #7530 @ 28.12.05 18:59
Mægtig god guide :) Kræver dog man sætter sig lidt ind i det.
Jeg tror bare der er en lille fejl i koden. Når jeg højreklikker og får min menu frem, og derfter kører musen væk igen, forsvinder menuen ikke. Jeg bliver nødt til at klikke i menuen for at den forsvinder!

Er der nogen der ved hvordan man får menuen til at forsvinde når man klikker et andet sted end lige på menuen?

- Tank
User
Bruger #2165 @ 24.01.06 23:48
Jeg kan se der er et par ubesvarede spørgsmål på dette emne, så jeg anbefaler stærkt at man læser nederste linie: "Du er velkommen til at stille spørgsmål i vores forum."
User
Bruger #10782 @ 07.11.06 11:38
den er ikk nem og forstår men det er ellers intressant
User
Bruger #9692 @ 17.10.07 23:07
Jeg får meddelsen :(
Compile Error:

Only pulic user defined types defined in public object modules can be used as parameters or return types for public procedures of class modules or as fields of public user defined types

Kan nogle forklare dette?:S
User
Bruger #9692 @ 17.10.07 23:16
Jeg får meddelsen :(
Compile Error:

Only pulic user defined types defined in public object modules can be used as parameters or return types for public procedures of class modules or as fields of public user defined types

Kan nogle forklare dette?:S
Du skal være logget ind for at skrive en kommentar.
t