I noticed that Dispose() doesn't release all GDI objects.
Try creating a new ToolTip in a loop, while also calling Dispose and GC.Collect() and you'll see that GDI objects are rising without being released.
I think it has something to do with windows messages processing used in the Dispose method.
If you use the auto upgrade to convert this to the 2008 format, you will find it no longer works properly. This has nothing to do with the code, but rather a change to the tooltip control implementation. The WM_NOTIFY message in the wndproc uses the NMHDR struct to get notifications on tooltip state changes via the 'code' struct member, in 2008, the 'code' integer always returns 0. I have no idea why this is, or what internal alteration precipitated the change, but I do have a simple fix for the code.
In the WndProc: WM_NOTIFY | WM_REFLECT case, add the following code:
nM.code = TTN_POP;
nM.code = TTN_SHOW;
So the beginning of the switch should now look like this:
case (WM_NOTIFY | WM_REFLECT):
NMHDR nM = new NMHDR(0);
RtlMoveMemory(ref nM, m.LParam, Marshal.SizeOf(nM));
if (nM.hwndFrom == _hTipWnd)
nM.code = TTN_POP;
nM.code = TTN_SHOW;
A really nice control, I've began using it after the windows ToolTip failed me miserably (not too hard to achieve that).
But, I'm having a problem here - I've registered to the RightMouseUp event and:
A. You have a small bug where you actually raise the RightMouseDown event instead (found easily via a Null reference exception).
B. SOMETIMES, can't really figure out yet when - the DrawEventArgs have invalid information, i.e. the Caption and other fields hold junk instead of the info of the clicked ToolTip. I noticed you get this info via a SendMessage but couldn't trace the source of the error.
first: thanks for this great control. I would like to to display the tooltip automatically from control load event handler. I tried Show method but it don't work. Is there any way to display control without hovering over bound control?
This is not what the 'show' command is for, (ex. would be: reshowing after a timeout, or manual mousemove trigger). Much of what is going on with timers and window itself, is being done inside (mfc)tooltip window class, not this hosting class, so mouse must be over control, and it must be visible in order to start timers. Not sure what you are trying to do, (intro window maybe?), but you could create a static window to do it.
Hi, I'm trying to show a tooltip in a modal dialog window fired from a VS add-in, but the tooltips don't work. Also, on my machine (Vista Ultimate), some of the borders and shadows of the tooltip drift a pixel or two from the tooltip itself. Do you know how it can be fixed?
Shadows are seperate windows drawn by the system not the tooltip or this class. I am also using Vista ultimate though, and it looks fine here. As for modal window from add in, are you passing in the handle of the dialog or the add in?
I have no idea what control/code you are using so.. what I would do, is set a breakpoint within the wndproc and see what messages are coming through. If messages are absent, then window target message pump might be consuming necessary messages. Also, some windows can not be subclassed, they will return no messages, I can't remember if system dialog is one of them, check msdn.
You make a convincing argument for the case of API over .NET and I'm not experienced enough to argue effectively either way. However, given the success of .NET there must be some advantage that translates to an advantage for the developer (besides being lazy enough to not want to learn the API calls). Arguably that advantage comes when performance and speed are not a critical factor. There are probably tens of thousands of small, targeted business applications written in .NET that work perfectly for the task with no perceptible disadvantage by the user - perhaps the true litmus test. BTW: excellent article, very well composed -- a true 5.
I think .Net methods offer a great many advantages, like solving many versioning issues (dll hell), regimentation and standardization of methods, and lends itself well to rapid deployment. I also think that the building block method of programming is the way of the future, as underlying systems evolve in complexity, it will necessitate a more distinct seperation between application developers and system designers. But.. we are not there yet. For the time being, I think in matters where speed and efficiancy are critical design elements in your application, the use of api must be considered, and that the best approach should be a marriage between .Net methods, and the use of api. The main point I am trying to drive home here is, that api is far from being depreciated, and is a very valuable tool in this language, and in a programmers skill set.
Congrats on the nice article and the efforts you have put in it (you've got my 5). I have one comment on the model you have chosen - you assume that as far as the Tooltip is Alive, it is associated with a Win32 Handle (a simple property set like Active uses the SendMessage API). However, you may want to use it in certain cases (like Design-time) where Handle is not needed. Or you may want to instanciate it and display it later on. I think it is a good practise to create (and destroy) its Handle on demand (like System.Windows.Forms.Control does).
I could move the createwindow call out of the constructor and allow it to be created manually, and I did consider this. The only problem is that I would have to test the handle for validity in every property/method, because developer may be attempting to set properties before creating the tooltip window with the creation method. Most implementations that I have seen (in C), create the tooltip as a standing member of the application, that is, it is alive and waiting from the point when the application is first initialized. The ToolTip class itself does not have a very large footprint, (basically a static window with timers and drawing functionality), so I think this is a reasonable way to implement the class.
It has nothing to do with the footprint - it is just a semantic used through all Windows Forms - there is a distinct separation between a Control and its Handle. The Control, as an object, may exists without a Handle created. Its Handle is created whenever needed, however you may alter its properties even if its Handle is NOT created (managed vs unmanaged resources). This gives you the ability to have a Design-Time support - as seen from 2 comments below, you will have troubles with instanciating your Tooltip at Design-Time. The Control class itself has CreateHandle and DestroyHandle methods which reallocate system resources and destroy them respectively.
So when are you gonna do a custom TabControl for us?
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
Grid is a listview hybrid, based on a control I wrote in vb6 some years back. Original (vhGrid on psc), was 27k lines of code, and very fast and featured. This iteration will hopefully outshine the other by quite a margin, and should be worth the wait..
I mean the same way the standard .NET tooltip can be used. So, add the tooltip to the VS toolbox and drag the tooltip on to the form. Now the properties of the tooltip can be set by VS instead of having to do this from within the code.
Don't think that will work. It derives from NativeWindow. The VS designer needs classes derives from Control or at least Component afaik. Some attributes and additional coding would be the least to achive this imo.
But honestly, even adding the standard .NET tool tips directly in the code is twice as fast as using the designer. Create instance, eventually change a property and call SetToolTip and you're done. With intellisense that take only 5 seconds.
I see.. you would have to drop this into a usercontrol, then add a subclasser class, hook up the subclass eventhandler and compile. Why bother though, the class is easy to wire up, create an instance and call SetToolTip for each control and your done.
I agree it can easily be done in the way you describe. But if it comes to localization it's much more work. Then you have to write all the plumbing code to obtain the resource value from the resource file and assign it to the tooltip. If it is all done from within VS, you just have to enter the text and it is automagically placed in a resource file.
"In my own implementation, I'll use SafeHandle rather than IntPtr for the window handle (why didn't they just fix the garbage collector?), but in an attempt to keep this available to pre 2.0 versions of C#, I am using IntPtr here."
You can not open a new tooltip in example until the last tip has closed. This is a limitation set in the example only. I used IsVisible to check if the tip had closed before allowing a new one to open, because the same tip is demonstrating many different styles, and so has to flip property settings before reappearing. Also. VS 2.0 users should change window from IntPtr to SafeHandle as described in the article.