Solved

Closing context menu in systray application

Posted on 2009-07-15
7
537 Views
Last Modified: 2013-12-04
Hi,

I've developed an application in VB6 that is really a systray application (it doesn't have a main screen, minimizing into the systray). I'm using the API function "Shell_NotifyIcon" to achieve this. Full code is included (clsSysTray). My app is some kind of listener, waiting for a barcode scanner to be plugged into an USB port.

From this class "clsSysTray", an event is raised, notifying my application that a mouseevent has occurred, like a right-click. My app. responds to that right-click by showing a context menu, using the PopupMenu function (note that the "m_frm_MouseMove" event is actually a callback received from the systray icon).

All goes fine, except when a user doesn't really click on any item within the context menu, but in stead of disappearing when it looses focus, it just sits there, waiting for the user to click on anything; it won't close! The only way to close it is to click on an item within it. Another major drawback is that my app. doesn't respond to any window events while the context menu is open (PopupMenu halts code until the menu item is closed).

I need a way to be able to close/hide the context menu item when focus is lost. This happens when the user clicks next to the context menu for example. The behaviour I expect is like the winamp agent's context menu. I'm open minded to solutions describing a whole other way of showing a popupmenu, like windows API functions or whatever...

I've also included all relevant code from frmMain, which is actually a dummy form which' handle will be used for the systray icon and which contains the menu item that is used as a context menu.


With kind regards,

Luc Derckx
'****************************************************
'*****************  clsSysTray  ********************
'****************************************************
 
Option Explicit
 
Private Declare Function Shell_NotifyIcon Lib "shell32.dll" _
( _
   ByVal dwMessage As Long, _
   lpData As NOTIFYICONDATA _
) As Long
 
Private Const NIM_ADD As Long = &H0
Private Const NIM_MODIFY As Long = &H1
Private Const NIM_DELETE As Long = &H2
Private Const NIM_SETFOCUS As Long = &H3
 
Private Const NIF_MESSAGE As Long = &H1
Private Const NIF_ICON As Long = &H2
Private Const NIF_TIP As Long = &H4
 
Public Enum MouseMessage
   WM_MOUSEMOVE = &H200
   WM_LBUTTONDOWN = &H201
   WM_LBUTTONUP = &H202
   WM_LBUTTONDBLCLK = &H203
   WM_RBUTTONDOWN = &H204
   WM_RBUTTONUP = &H205
   WM_RBUTTONDBLCLK = &H206
End Enum
 
Private Type NOTIFYICONDATA
   cbSize As Long
   hwnd As Long
   uID As Long
   uFlags As Long
   uCallbackMessage As Long
   hIcon As Long
   szTip As String * 64
End Type
 
Public Event OnTrayEvent(Message As MouseMessage, Shift As Integer)
 
Private WithEvents m_frm As VB.Form
Private m_mnu As VB.Menu
 
Private m_blnTray As Boolean
Private m_IconData As NOTIFYICONDATA
 
Public Property Get InTray() As Boolean
   InTray = m_blnTray
End Property
 
Public Sub MoveToTray(Form As VB.Form, Optional Icon As IPictureDisp, Optional ToolTip As String)
   If Not m_blnTray Then
      With m_IconData
         .cbSize = Len(m_IconData)
         .hwnd = Form.hwnd
         .uID = vbNull
         .uFlags = NIF_ICON Or NIF_MESSAGE Or NIF_TIP
         .uCallbackMessage = WM_MOUSEMOVE 'This will have the form "Form" raise MouseMove events
         .hIcon = IIf(Icon Is Nothing, Form.Icon, Icon)
         .szTip = IIf(ToolTip <> "", ToolTip, App.Title) & vbNullChar
      End With
      
      m_blnTray = Shell_NotifyIcon(NIM_ADD, m_IconData)
      
      If m_blnTray Then Set m_frm = Form
   End If
End Sub
 
Public Sub RemoveFromTray()
   If m_blnTray Then m_blnTray = Not Shell_NotifyIcon(NIM_DELETE, m_IconData)
End Sub
 
Public Sub ModifyTrayIcon(Icon As IPictureDisp, Optional ToolTip As String)
   If m_blnTray Then
      m_IconData.hIcon = Icon
      If ToolTip <> "" Then m_IconData.szTip = ToolTip & vbNullChar
      
      Shell_NotifyIcon NIM_MODIFY, m_IconData
   End If
End Sub
 
Private Sub Class_Initialize()
   m_blnTray = False
End Sub
 
Private Sub Class_Terminate()
   Set m_mnu = Nothing
   Set m_frm = Nothing
   
   If m_blnTray Then RemoveFromTray
End Sub
 
Private Sub m_frm_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
   Dim msg As MouseMessage
   Dim blnCancelPopup As Boolean
   
   If m_blnTray Then
      msg = X
      RaiseEvent OnTrayEvent(msg, Shift)
   End If
End Sub
 
'****************************************************
'***************  frmMain (stripped)  ***************
'****************************************************
 
Private WithEvents m_objTray As clsSysTray
 
Private Sub Form_Load()
   'iml32Status is a 32-bit image list from vbAccellerator
   Set m_objTray = New clsSysTray
   m_objTray.MoveToTray Me, iml32Status.ItemPicture(li), App.Title
End Sub
 
Private Sub m_objTray_OnTrayEvent(Message As MouseMessage, Shift As Integer)
   If Message = WM_RBUTTONUP And Shift = 0 Then
      '...
      'Doing some menu setup here, like enabling/disabling items based on app. status
      '...
      
      'mnuContext is a standard menu item pre-made designtime by the built-in VB menu editor.
      PopupMenu mnuContext   'Code halts here, until item has been chosen
   End If
End Sub

Open in new window

0
Comment
Question by:IThema
  • 3
  • 2
7 Comments
 
LVL 10

Accepted Solution

by:
cool12399 earned 500 total points
ID: 24866481
Without getting into some really convoluted programming, a simple solution is to use a timer in your app.

I am assuming of course whenever a mouse is put over a menu item, an event is fired, or that the user should make a decision within a few seconds, etc.

If nothing happens, say for 5 seconds, then it 'auto' closes.

I.e.,

Timer1.interval = 5000 ' 5 seconds
timer1.enabled= true
...
do stuff
...


private sub timer1_timer
   timer1.enabled=false
   closeContextMenu()
end sub
0
 
LVL 5

Author Comment

by:IThema
ID: 24866996
Hi Cool12399,

I've was doubtfull wether the OnTimer() event would be handled, because the app. halts on the PopupMenu() function, but it actually DOES handle the event.

So, I guess this will work, although I don't know how the closeContextMenu() would look like. I wasn't able to find any built-in function that closes a context menu.

Also, I'd still like to have some other solution, because I don't really like the idea of my app. limiting the time for the user to make a selection, where it is the app. that determines how long this is.

I'd like to take another approach here, where my app. detects when the context menu / tray icon loses focus in any way and then closes it.

The thing is: I know it's possible because other tray apps. behave the same way. I think I will, however, apply your suggested solution if I'm ubable to come up with anything else, so I do in fact thank you for the suggestion.
0
 
LVL 10

Expert Comment

by:cool12399
ID: 25109083
HI,

I did answer the question (as the original poster acknowledged it was a solution that worked, although not the one he wanted, this would work), so please accept it.

Thanks
0
 
LVL 10

Expert Comment

by:cool12399
ID: 25109085
HI,

I did answer the question (as the original poster acknowledged it was a solution that worked, although not the one he wanted, this would work), so please accept it.

Thanks
0
 
LVL 5

Author Comment

by:IThema
ID: 25112127
Hi,

I Don't like the forced accept. It's not because of the points I've lost. I really couldn't care less about that. My problem here is that there's no solution provided here. Also, there's almost no timeframe for me to respond to the objection posted by cool: it was honoured in 3 hours!

Cool, I didn't say it was a solution that worked. Quote: "So, I GUESS this will work, although I don't know how the closeContextMenu() would look like. I wasn't able to find any built-in function that closes a context menu". Even so, it was not the solution that I asked for. I said I didn't like the idea. Quote: "...because I don't really like the idea of my app. limiting the time for the user to make a selection, where it is the app. that determines how long this is"

So, without me knowing how to force close a menu item, I'm unable to even test your suggestion. You didn't provide me with a way showing me how to do so. I just might've awarded you some fair points if you did.

I still haven't been able to solve the problem, so this "Accepted answer" is just a stain in the EE knowledge base. It's really a shame to the admin too and I'm really sorry for saying so, because of my good experiences I had in the past.

If I may make a recommendation: just delete this quotion; don't stain EE's knowledge base. Fairly, points should be refunded also.

Cheers.
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Enums (shorthand for ‘enumerations’) are not often used by programmers but they can be quite valuable when they are.  What are they? An Enum is just a type of variable like a string or an Integer, but in this case one that you create that contains…
You can of course define an array to hold data that is of a particular type like an array of Strings to hold customer names or an array of Doubles to hold customer sales, but what do you do if you want to coordinate that data? This article describes…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question