Hello everybody. I'm trying to display a balloon tooltip close to one textbox, but so far no luck. What am I doing wrong? Here is the code:
Code:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Const ECM_FIRST As Long = &H1500
Private Const EM_SHOWBALLOONTIP As Long = (ECM_FIRST + 3)
Private Const EM_HIDEBALLOONTIP As Long = (ECM_FIRST + 4)
Private Type BALLOONTIP
cbStruct As Long
pszTitle As String * 10
pszText As String * 10
tIcon As Long
End Type
Private Sub Text1_Click()
Dim a As BALLOONTIP
a.cbStruct = Len(a)
a.pszText = "fdc"
a.pszTitle = "fdc"
Debug.Print SendMessage(Text1.hwnd, EM_SHOWBALLOONTIP, 0&, a)
End Sub
There are balloon tip projects in the code bank/utility bank portion of this forum.
If you read the MSDN documenation on EM_ShowBalloonTip, you will see that it requires a manifest file and it is only available on XP and above.
Here is a working example, if a manifest file is provided; note the changes I made to the structure and how it is populated:
Code:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Const ECM_FIRST As Long = &H1500
Private Const EM_SHOWBALLOONTIP As Long = (ECM_FIRST + 3)
Private Const EM_HIDEBALLOONTIP As Long = (ECM_FIRST + 4)
Private Type BALLOONTIP
cbStruct As Long
pszTitle As Long
pszText As Long
tIcon As Long
End Type
Private Sub Text1_Click()
Dim a As BALLOONTIP
Dim sTitle As String, sText As String
sText = "Hello World"
sTitle = "Sample Tip"
a.cbStruct = Len(a)
a.pszText = StrPtr(sText)
a.pszTitle = StrPtr(sTitle)
a.tIcon = Me.Icon.Handle
Debug.Print SendMessage(Text1.hwnd, EM_SHOWBALLOONTIP, 0&, a)
End Sub
Insomnia is just a byproduct of, "It can't be done"
That's too complicated. I think I'll be using VB's tooltips, with their Tooltiptext property. However, there is another problem. I'm designing an enhanced richtextbox with transparent buttons in the text area for bold, italics etc. In order to make these buttons work properly, without interfering with the selected part of the text, I had to subclass the ButtonDown and ButtonUp events in the RTB. Say the user selects part of the text and then they want it to become boldface, they click on the "bold" button and they get what they want without changing the selection (which would happen if the click event wasn't subclassed: remember that the buttons are in the text area).
Now, I wish to add tooltips to the buttons. To this purpose, I'm gonna write code in the RTB MouseMove event, something like:
If x is between x1 and x2, and y is between y1 and y2 then text1.tooltiptext="bold"
and so on. Now comes the issue. The click event in the buttons area is filtered out by the subclassing, so when the user clicks the button the tooltip won't go away. In order to fix it, I need to send the RTB the right message with SendMessage. And what's the message I have to send to make a tooltip disappear?
I'm not completely sure I understand the layout. The buttons are actually on the RTB? Therefore, they are actual controls with the .hWnd property? Think so.
The tooltip appears because 1) the property is set and 2) the mouse is over the button. Can't you simply remove the tooltip property (i.e., set it to "" ) after the button is clicked (buttonup event), then track the mouse (there is an api you can use that will send a message to your subclasser) and when the mouse leaves the button area, replace the tooltip. Just an idea. The API I was referring too: TrackMouseEvent
Insomnia is just a byproduct of, "It can't be done"
That was indeed a stupid problem. I only have to set Tooltiptext=vbNullString when the mouse button is clicked, as you said. However, it doesn't work very well. I'm sending attached a very preliminary draft of my project. The RTB is transparent and higher in ZOrder than a picbox, which is gonna display the buttons. The latter will simply be icons, and I'm gonna track the mouseup on each of the "buttons" via the lParam in the subclasser. So far, I'm only tracking when the mouseup is raised on the bottom part of the RTB. In this case, the underlying picture is gonna change (now I'm using random pictures, in the future the button is gonna take on the "pushed" state) and the selection becomes bold. In addition, when the mouse pointer hovers in the bottom area, a random tooltip is displayed, which disappears on mouseup, as well as when the mouse leaves the hot area. The problem I'm having is that when the mouse moves to the top part, some crap shows up, even though for a very short time. This is gonna be annoying to the end user. How can I fix it?
This seems to be an issue with RTBs. Just try this code:
Code:
Private Sub RichTextBox1_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
If y > 500 Then
RichTextBox1.ToolTipText = "a"
Else
RichTextBox1.ToolTipText = ""
End If
End Sub
Move the mouse and when the tooltip disappears you'll see something unpleasant.
1. Changing the backcolor of the tooltip is a system color. If you change it, then it changes for every application on the system.
2. If the "unpleasant" stuff are artifacts from when the tooltip disappears, maybe calling the InvalideRect API will force it to repaint a bit faster.
I will play with this tomorrow when I find some time.
Edited: Had a little time this A.M. before the family woke.
1. That little square the appears after the tooltip is hidden... That is what you are trying to avoid? Not sure how to avoid it; played with your sample for a few minutes, tweaking this and tweaking that -- no success.
2. I think you will have far more control over a tooltip/balloon tip if you create your own. Granted, more complicated, as you mentioned earlier, but more control.
3. As a side note. Are you really sure you want the "buttons" to appear under the rtb that way? What happens to the buttons when the rtb text is large enough to overlap them? I think it would be somewhat annoying to see the text overlapping the buttons but that is my opinion. The question is obvious, why not create your own toolbar or are you just trying to do something different?
Last edited by LaVolpe; May 23rd, 2009 at 07:28 AM.
Insomnia is just a byproduct of, "It can't be done"
Yeah, that little square is annoying. As for the buttons, it's unlikely that the text will overlap them. The textbox is gonna be far larger than really needed. So can you show me how to make my own tooltips? I would like to avoid using a manifest file, but if it's really needed, then can you suggest how to write it? I've never even heard of a manifest file... Thanks a lot.
Simple way of tool top is a borderless, captionless, controlbox less form with a label equal to form dimensions or inversly the form is the size of the label as determined by the textwidth of the text. Autosize = true may help or hinder.
Form level variables
Dim TT As CTooltip
Dim m_bInLable as boolean
Form load
Set TT = New CTooltip
TT.Style = TTBalloon
TT.Icon = TTIconInfo
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Not m_bInLable Then
m_bInLable = True
TT.Title = "Information"
TT.TipText = "Over Command1"
TT.Create Command1.hWnd
End If
Exit Sub
End Sub
Turn off tooltips
Private Sub Myrtf1_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
m_bInLable = False
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
m_bInLable = False
End Sub
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Which property of the form are you referring to? There's no AlwaysOnTop...
Plus, I can't understand that code. It seems that if you call the Create method multiple times while moving over command1 (i. e. getting rid of the m_bInLable condition) the tooltip won't pop up. Why? The Create method every time destroys the previous window and creates a new one
Code:
If m_lTTHwnd <> 0 Then
Call DestroyWindow(m_lTTHwnd)
End If
m_lTTHwnd = CreateWindowEx(0&, _
TOOLTIPS_CLASSA, _
vbNullString, _
lWinStyle, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
0&, _
0&, _
0&, _
0&)
If the old window is destroyed and a new one is created, why doesn't it show up if you get rid of the m_bInLable condition?
Which property of the form are you referring to? There's no AlwaysOnTop...
Plus, I can't understand that code. It seems that if you call the Create method multiple times while moving over command1 (i. e. getting rid of the m_bInLable condition) the tooltip won't pop up. Why? The Create method every time destroys the previous window and creates a new one
Code:
If m_lTTHwnd <> 0 Then
Call DestroyWindow(m_lTTHwnd)
End If
m_lTTHwnd = CreateWindowEx(0&, _
TOOLTIPS_CLASSA, _
vbNullString, _
lWinStyle, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
0&, _
0&, _
0&, _
0&)
If the old window is destroyed and a new one is created, why doesn't it show up if you get rid of the m_bInLable condition?
What ever is next to the item
Form mousemove, or picture_Mousemove set this to false
m_bInLable = False
Only if your form had a AlwaysOnTop
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Sorry, I'm understanding nothing. Can you please reread my last post and reply to my questions. What's AlwaysOnTop? Why the boolean flag?
AlwaysOnTop is a form decaration telling your form to display above all other forms or windows. I have quite a few like that. If you don't have the declaration don't worry about the tool tip will be on top.
The boolean flag resets the tool tip.
Last edited by isnoend07; May 25th, 2009 at 02:41 PM.
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
1) AlwaysOnTop. What's a form declaration? Do you mean an API declare like SetWindowPos or something?
2) Boolean flag. Replace this code
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Not m_bInLable Then
m_bInLable = True
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End If
'Exit Sub
End Sub
with this code:
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End Sub
Why in the second case the tooltip doesn't show up?
Sorry, I'm understanding nothing. Can you please reread my last post and reply to my questions. What's AlwaysOnTop? Why the boolean flag?
As Edgemeal pointed out:
Change this
m_lTTHwnd = CreateWindowEx(0&,
to this
m_lTTHwnd = CreateWindowEx(WS_EX_TOPMOST, _
The tooltip will always be on top regardless if the form is TopMost
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
1) AlwaysOnTop. What's a form declaration? Do you mean an API declare like SetWindowPos or something?
2) Boolean flag. Replace this code
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Not m_bInLable Then
m_bInLable = True
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End If
'Exit Sub
End Sub
with this code:
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End Sub
Why in the second case the tooltip doesn't show up?
A form declaration is where you place things you want all your subs, functions to see.
That is why this is put at the top:
Dim m_bInLable as boolean it can be seen by any place on the form
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If m_bInLable = False Then
m_bInLable = True
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End If
'Exit Sub
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
m_bInLable = False to allow the tooltip to be recreated
End Sub
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Okay, got the AlwaysOnTop thing. It involves SetWindowPos as I expected. Now, coming to the second question, I still can't understand why the tooltip doesn't show up if you simply write
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End Sub
Okay, got the AlwaysOnTop thing. It involves SetWindowPos as I expected. Now, coming to the second question, I still can't understand why the tooltip doesn't show up if you simply write
Code:
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
TT.Title = "Information"
TT.TipText = CStr(X)
TT.Create Command1.hWnd
End Sub
You need to look at the class to see how it works, by setting to True they are destroyed after a certain time. Setting them to false allows them to be created or recreated
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
It's just because I looked at the class that I don't understand. The tooltips are destroyed everytime a new one is created, not "after a certain time". There are no timers whatsoever. Every time you call the Create method, it destroys the current tooltip window and creates a new one. However, I can't understand why you have to exit the control before calling the Create method again. The boolean flag has just this effect: it allows you to call Create again only after you exited the control. It seems that if you can't call it twice or more without exiting the control, and I just can't understand why...
Last edited by dichelson; May 25th, 2009 at 05:03 PM.
There is no timer it uses the
Private Declare Function SendMessageLong Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
What is the tooltip not doing correctly
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Nothing irregular. I just didn't understand the code, but now I do. When the mouse is hovering over the control, it continually sends MouseMove messages, which continually calls the Create method. This destroys every window before it shows up, then creates a new one which is destroyed by the next Create call shortly after. This is why the boolean flag is needed, to prevent this from occurring.
Now I have another question (sorry if I bother you). Why did you use Empty in the class code, e. g. If mvarForeColor <> Empty Then? Why didn't you just write If mvarForeColor <> 0? This sounds exotic to me...
Nothing irregular. I just didn't understand the code, but now I do. When the mouse is hovering over the control, it continually sends MouseMove messages, which continually calls the Create method. This destroys every window before it shows up, then creates a new one which is destroyed by the next Create call shortly after. This is why the boolean flag is needed, to prevent this from occurring.
Now I have another question (sorry if I bother you). Why did you use Empty in the class code, e. g. If mvarForeColor <> Empty Then? Why didn't you just write If mvarForeColor <> 0? This sounds exotic to me...
I did not write that code, got it somewhere 2 or 4 yrs ago. At that time i was more in touch with it as you are now. It's always worked, so i just uploaded to help. My ongoing project has grown to about 65 forms, 65 bas modules and 35 classes. I will look at what you are seeing after the lakers-Nuggets game starting in a couple
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Thanks for the tip, working on my tooltips as i answer. Had to pay a renta coder to make the tooltip show on the text portion of a combobox
Here's another workaround I use....
When you have controls right next to each other (where the flag/balloon can't get reset by moving the mouse) you'll notice the balloon won't be shown. So in my showtootip sub I have it compare the last handle passed to it and reset the balloon/flag if needed.
Code:
Public Sub ShowToolTip(ToolTip As String, LHwnd As Long, Optional Title As String = "Main Program Title Here")
' // show tooltip //
Static lastHndle As Long
If lastHndle <> LHwnd Then
lastHndle = LHwnd
ToolTipOff
End If
If m_bInLable = False Then
m_bInLable = True
Tt.Title = Title
Tt.TipText = ToolTip
Tt.Create LHwnd
End If
End Sub
Public Sub ToolTipOff()
' // kill balloon if exsist //
If m_bInLable = True Then
Tt.Destroy
m_bInLable = False
End If
End Sub
When you have controls right next to each other (where the flag/balloon can't get reset by moving the mouse) you'll notice the balloon won't be shown. So in my showtootip sub I have it compare the last handle passed to it and reset the balloon/flag if needed.
Code:
Public Sub ShowToolTip(ToolTip As String, LHwnd As Long, Optional Title As String = "Main Program Title Here")
' // show tooltip //
Static lastHndle As Long
If lastHndle <> LHwnd Then
lastHndle = LHwnd
ToolTipOff
End If
If m_bInLable = False Then
m_bInLable = True
Tt.Title = Title
Tt.TipText = ToolTip
Tt.Create LHwnd
End If
End Sub
Public Sub ToolTipOff()
' // kill balloon if exsist //
If m_bInLable = True Then
Tt.Destroy
m_bInLable = False
End If
End Sub
good tip i just moved a couple option buttons to provide space
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
I did not write that code, got it somewhere 2 or 4 yrs ago. At that time i was more in touch with it as you are now. It's always worked, so i just uploaded to help. My ongoing project has grown to about 65 forms, 65 bas modules and 35 classes. I will look at what you are seeing after the lakers-Nuggets game starting in a couple
A long data type is empty if it's 0
Both would work
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
However, I have to figure out some workaround, because in my project I need different tooltips for different coordinates on the same control. Obviously, that class doesn't work for that. I was thinking about storing the last coordinates into a static variable and using a threshold for the difference between that variable and the current coordinates in place of the boolean flag. Course this only works if the user moves the mouse quickly enough. I have a feeling that this is gonna suck...
However, I have to figure out some workaround, because in my project I need different tooltips for different coordinates on the same control. Obviously, that class doesn't work for that. I was thinking about storing the last coordinates into a static variable and using a threshold for the difference between that variable and the current coordinates in place of the boolean flag. Course this only works if the user moves the mouse quickly enough. I have a feeling that this is gonna suck...
Don't what kind of controls you are using. Could you pace a transparent label or shape control over your control?
I was going to add a bordless textbox over the text portion combobox until i found a work around. Combobox do not have a mousemove event.
Waiting for a full featured smart phone with out marrying a provider
Go Android
Go raiders
Shape controls don't have any events. If I place shapes on top of my control, then there is no way to capture the mousemove on them. Plus my control already consists of a transparent RTB on top of a picbox. Am I gonna make a sandwich? I'm already having ZOrder problems with that...
i use similar class to this one for displaying tooltips on flexgrid cells.
This class can be used for a couple of controls.
I didnt wrote this code but i found it not so long ago.
The question is why if you open explorer window in windows xp and press F3 button on that for searching, this and mine class works for few secounds after that? but when you close the search window works again.