I have a hierarchical flexgrid control with the ToolTipText property set, and when I run from source the tooltip displays as it should. But when I compile it and run that way, the tooltip doesn't display.
I've tried to remove anything listening to MouseMove in the hopes that that would fix it, and when I add some code to put the tooltip text into a message box, it appears to be set correctly. Can anyone think of why this would be happening?
Update: It appears that the problem arises when I host the grid inside another user control. E.g.: make container.ctl, which is just a blank control but with ControlContainer = True. Then make gridholder.ctl, which is a mshfg inside of a container.ctl. Lastly, embed gridholder.ctl into some form. Tooltips on the flexgrid don't appear to show up.
I'm interested to see how reproducible this is...
I haven't found a workaround for this issue yet, but I have a better idea of why it's happening after some testing and stepping through some of the VB6 runtime code in WinDBG.
The first interesting thing is that VB6 doesn't use the standard tooltip display mechanisms provided by Windows. For example, it doesn't use WM_NOTIFY messages to show/hide tooltips, or any of the other "standard" tooltip support described in the documentation explaining how tooltips work in Windows.
Instead, the VB6 runtime has its own way of managing and displaying tooltips. In principle, it's similar to in some ways to the standard Windows way of dealing with tooltips, but it's also different in a quite a few areas.
A breakdown of how VB6 does tooltips:
When a VB6 program starts, the runtime uses SetWindowsHookEx to install a mouse hook for the program's main thread.
The mouse hook intercepts all mouse messages sent to the program, in particular all WM_MOUSEMOUSE messages
Whenever the mouse hook runs, it calls an internal method in the VB6 runtime to get the object pointer (HCTL) of the control that the mouse is currently over top of. Note that this is an actual COM interface pointer, not a window handle.
It translates the HCTL to the corresponding window handle (HWND).
It checks to see if the mouse position is within that window's rectangle.
If so, it retrieves the ToolTipText property for the control. If this is not empty, it creates a tooltip window and displays the tooltip after a 700ms delay.
The problem with the MSHFlexGrid (and I imagine other controls that are not standard VB6 controls) is that this code doesn't retrieve the correct HCTL when you hover over the control and it's inside a custom container.
In that case, the code retrieves the HCTL of the custom container, not the HCTL of the MSHFlexGrid itself. Therefore, it retrieves the container's ToolTipText property (which is empty) and not the grid's ToolTipText, and therefore won't display a tooltip.
I'm not sure exactly why it does this, because as noted in the comments on your question, all of this works correctly if you use a PictureBox as your container.
I suspect the PictureBox has code to handle this correctly that is not included when you create your own container.
I'll update this answer with an actual workaround if I can find one. The only thing I can think right now is to somehow "sync" your container's ToolTipText property with the grid's ToolTipText property, so that when VB6 requests the container's ToolTipText, it will return the value of the grid's ToolTextTip property instead.
This is easier said than done, however, because ToolTipText is an extender property, and extender properties take precedence over properties that you write yourself that have the same name.
After a bit of research, I found what I think is the underlying problem. Your user control is not implementing any method for the controls to interact with. User Controls that are Container Controls need to implement the Extender functionality. These two links are the best I've found on the subject so far.
http://www.justvb.net/obook/ch7.htm#UsingtheExtenderObject
http://msdn.microsoft.com/en-us/library/aa733622(v=vs.60).aspx
Related
I'm trying to handle background color properly in a dynamically generated property sheet in dynamically generated property pages in win32 api using MFC (though I expect my question is general, and not restricted to MFC, but since my code and examples use it, it's germane to my question anyway).
So we have a:
CPropertySheet
containing multiple
CPropertyPage
I generate the contents of any given page dynamically - from file resources using a custom dialog definition language - all irrelevant other than to say - a list of controls and their coordinates is created within a given page, and the page is resized to accommodate them. This logic is working beautifully.
However, what doesn't work is that the controls and background of each page draws using the dialog default color/brush.
I've tried a number of ways to attempt to force it to draw using the white color/brush that a hard-coded property sheet / page would.
There are two important pieces to this:
Page Background
Control (on the page) background
For #1, I've tried:
acquire the background brush from parent window class (it's dialog bkgrd) (same is true if I do this and ask the tab control)
change the property page to use WS_EX_TRANSPARENT (PreCreateWindow is not called by the framework when generating a page viz PropertySheet::AddPage)
For #2, I've tried:
overriding OnWndMsg / WM_CTLCOLORSTATIC to forward that request to (A) the parent (sheet), and (B) to the tab control (which is what wants the white in the first place).
However, anytime I use any of the above "ask for the background / forward the request" up the chain to either the sheet or the tab control - I get the dialog background color, never the white I'd expect.
Using Spy64, I can see that for a fully hardcoded property sheet / page - that the only discernable differences I can see is that the dialog window created in AddPage (or its moral equivalent) has WS_CHILD instead of mine which has WS_POPUP (the rest of the styles appear to be the same, such as WS_VISIBLE|DS_3DLOOK|DS_FIXEDSYS|DS_SETFONT|DS_CONTROL and WS_EX_CONTROLPARENT.
So, other than the WS_CHILD, I see no significant differences from what I'm creating and from another property sheet that works properly from a standard resource (i.e. hard coded).
I'm also flummoxed as to how this works normally anyway - since forwarding things like the ctrlcolor message doesn't respond correctly - and asking for the windows background colors similar doesn't - then how is it in a standard case the background colors of controls and pages comes out as white, and not dialog background?
Any ideas or help would be appreciated - I'm kind of running out of ideas...
When Visual Styles were added in Windows XP they really wanted to show off this new feature so they made the tab background a gradient (really a stretched image) instead of a single color and this caused problems in old applications that did custom drawing with the dialog brush as the background.
Because of this, only applications with a comctl32 v6 manifest got the new look but there was a problem; old propertysheet shell extensions would load in new applications (including Explorer) and things would look wrong.
To work around this they also require you (or your UI framework) to call EnableThemeDialogTexture(.., ETDT_ENABLETAB) to get the correct tab page look.
As if things are not tricky enough, there is a undocumented requirement that you also need a button or a static control on the page!
If you have custom controls they have to call DrawThemeParentBackground when you draw if they are partially transparent.
Turns out my old code had defined an ON_WM_ERASEBKGND handler - and removing that (and all of my above attempts) makes it work.
So simply doing NOTHING is the correct answer. D'oh!!!
I'm leaving my shame here in case someone else trips on this! [Whoops!]
(Still interested if anyone has deeper insight into how this mechanism works under the hood)
I am in the process of writing a super classed version of the Windows LISTBOX common-control to add extra functionality.
A standard control sends the WM_CTLCOLORLISTBOX message to its parent which allows both its text and background colours to be specified at run time within an appropriate message handler. However WM_CTLCOLORLISTBOX is not sent to the control itself, therefore cannot be encapsulated and handled internally.
The scenario I am attempting to address is to change the background and text colours depending on the control's enabled/disabled state. The standard behaviour of leaving the listbox background the same shade regardless of its state looks ugly and inconsistent to me. Is there another way to set these values within the encapsulation, yet hand-off all other painting tasks to the base-class window procedure?
I wondered about using SetClassLongPtr(). However, not only would this not address the text colour but if I understand rightly it would change the background for ALL controls of that class currently in existence and not the specific control whose state has changed.
The answer should be obvious - since WM_CTLCOLORLISTBOX is sent to the parent window, you have to subclass the parent window in order to receive the message. There is no getting around that. However, some wrapper UI frameworks, like VCL, are designed to forward such messages to the control that generated then, so controls can handle their own messages. But if you are not using a wrapper framework and are working with Win32 directly, you have to handle the parent messages yourself. Refer to MSDN for details about subclassing a given HWND:
Subclassing Controls
I am trying to show a web page in a dialog control. All is working fine till I maximize the parent window, the inner control with webpage retains its size and so a blank area is left at the side of window. I want to know is there any WS_* message or something I can use to auto resize control when we resize the main window. I am using resource hacker so may be there can be some trick I should know.
Any help is appreciated.
Thanks.
The application must perform the resize. And it does so when it receives a WM_SIZE message for the parent control.
You are not going to be able change this by modifying the resources in a pre-existing binary. You are going to need to write some code to respond to that message.
The way I'd try seeing as I've never tried to hack something like this in.
Would be to see if the web control had an anchors property.
Its should be being passed a wm-size message, it's just not doing anything with it.
If you anchor all four corners of the webcontrol it should resize relative to it's parent.
The other way this is done is through explicit code that handles a resize event, don't think you could hack that in very easily though.
I'm trying to work out how to catch a data binding in the act (either intercept or post-process) to customize the display of data in the target control.
I know about IValueConvertor and understand that I can transform a simple value into another simple value, but I don't believe this is enough for my needs... which are:
The control in this case is a TextBlock and the data values from the objects in my ObservableCollection are variable length strings. I want to render the strings in multiple colours by splitting them into pieces and programatically creating a <Run Foreground="xxx" Text="yyy"/> for each piece inside the TextBlock.
Since the strings are variable length and the colours have to be programatically determined from the content of the string, I don't believe I can pre-create the <Run>s in the XAML, so I have to somehow get in on the data binding action and generate the <Run>s at bind-time (or very soon after).
Binding.NotifyOnTargetUpdated would seem to be a way to set up an event handler to do the work, but that's not available in the Windows Phone cut-down Silverlight implementation.
Any ideas? All search results seem to point to the above, but I'm looking for that little bit more.
Having apparently exhausted all the cut-down Silverlight on Windows Phone options for programmatically hooking the rendering of ListBoxItems via the data model, I ended up adding a Loaded="..." event handler to the XAML for the TextBlock.
It doesn't feel like the nicest solution, but perhaps that's just my code-preference talking and it's actually the right way to do it on Windows Phone.
In any case, because I'm hooked to the TextBlock directly, I'm not sure how to then get to the databound object on the ListBoxItem containing the TextBlock... if anyone has advice on how to get back up the tree to the generated ListBoxItem then I could use the bound object directly instead of retrieving it from elsewhere.
Note that since the ListBoxItem is generated, I didn't find where to put a Loaded="..." event handler for that in the XAML. The ListBox.ItemTemplate doesn't accept a Loaded attribute.
Update: this doesn't work!
The Loaded event handler fires when the TextBlock is first created and loaded, so the substitution works initially.
BUT
The generated ListBoxItem seems to be recycled (I guess by the ListBox.ItemContainerGenerator which doesn't want to use excessive amounts of memory by instantiating a whole new container when there are many off-screen entries in the list that won't need to be seen for a while) and when this happens, the Loaded event DOES NOT FIRE.
Since I modify the content when the TextBlock was first Loaded, this breaks the binding association so when the ListBoxItem is recycled, it now contains old/incorrect data.
Still no solution.
I'm thinking about trying to use an IValueConvertor and somehow pass a reference to the binding target... now sure how yet though.
Update 2: finally got it to work...
Sticking with the Loaded="..." event handler, it is possible to disable recycling of previously-generated ListBoxItems by configuring the VirtualizingStackPanel used by the ListBox under the covers.
In the XAML for the ListBox set VirtualizingStackPanel.VirtualizationMode="Standard" to force a new ListBoxItem to be generated each time instead of recycling previously-generated ones.
This means the Loaded event handler is called every time and I can replace the ordinary text of the TextBlock with the <Run>s to produce dynamically coloured text.
To modify a window of another program, I need to find a specific SysTreeView32 in it using EnumChildWindows API call.
When I inspect the window using Spy++, there are a number of SysTreeView32's in it but all are greyed out except one, which is the one I'm looking for.
The following picture is an example of grey items:
Why are the shown items gray and what API call does Spy++ use to know whether it should grey out an item or not?
Those are just non-visible windows - ie HWNDs that don't have the WS_VISIBLE style bit set. They are often worker windows - windows that just exist to process various messages in the background - or in some cases are UI that's yet to become visible. For example, a window that lets you hide or show a toolbar may just hide it by making it invisible rather than destroying it and recreating it later.
In your specific case, the WorkerW could be a placeholder for some other piece of UI that's not needed right now, while the msctl_statusbar32 looks like it's a hidden status bar.