I have seen the following TVN_BEGINLABELEDIT event handling:
RECT rect={0};
TreeView_GetItemRect(hwnd, hitem, &rect, FALSE);
InvalidateRect(hwnd, &rect, TRUE);
Editing a tree view label without the above code works fine. Is it redundant or there may be a situation where such handling is necessary ?
The Remarks section of the TVN_BEGINLABELEDIT contains hints about a treeview control's internals:
When label editing begins, an edit control is created [...]
By default, that edit control is positioned and sized to cover the item that is to be edited. There is no immediately obvious reason to invalidate an area that's going to be covered by another control right away.
It doesn't even make sense to invalidate the area covered by the item, if the implementation chooses to provide a custom size and position for the edit control. By the time the WM_PAINT message gets handled, the treeview control still holds the initial value for that item, so parts not covered by the edit control merely get redrawn as they were.
Related
I'm currently working on a project that utilizes an MDI Form. What I would like to achieve is that when the user hovers over one of the menu options in the MDI menu, a ToolTipText appears describing what can be found in said menu. I'm aware of the fact that in some options for VisualBasic 6, ToolTip is supported. However, I can't seem to add this to the MDI Form, or add a label control for that matter. Has anyone ever run into this problem, and if so, is there a workaround or a solution?
Please let me know if you have any additional questions or comments. Thank you in advance.
I'm not aware that any true menus on a VB6 for (even non-MDI) have a tool-tip property; hence, neither does the MDIForm when one its child forms has its menu displayed.
However, there are a few controls that can be put on the MDI parent form. The most useful of these is probably the PictureBox, into which you can then place any type of control, such as CommandButtons. It must be "docked" to either the top or bottom of the MDIForm, using the PictureBox.Align property. Controls within the PictureBox can be located any way you wish, and can have their normal ToolTip properties set.
Another MIDForm-usable control that I've had good luck with is the ToolBar control, which comes in as part of the CommonControls component. Like the PictureBox, it has a .Align property to set it to the top or bottom of the MDIForm. You can put any reasonable number of "buttons" on it, and have reasonable control over their appearance (graphics, etc.); apropos the OP's query, you can assign a ToolTip to each button. It wouldn't be difficult to have the buttons change depending upon the currently-active form, just as the MDI menus normally do.
One other alternative that can be considered (but may be quite tedious to implement) could be reading the Mouse.X and Mouse.Y properties for each MDI menu and setting the captions of a Label control (or TextBox) using the Mouse Hover event.
You'd need to find the top left and bottom right points for each MDI menu and if it is within the box, you can show your label control with the intended caption. But note that you can't just put any controls on an MDI Form except maybe PictureBoxes, Toolbars and Coolbars, etc.
So the Label or TextBox control would have to be first placed on top of a PictureBox control which can be top aligned.
I'm faced with a problem where I need to display some characters in a tree-view item (those belonging to the Symbol charset) using Symbol font while others in the default System font (Segoi UI on my Windows 7).
Custom draw allows us to draw different items using different fonts, but I would like to draw the same item string using different fonts as it applies to each character in the string as told above.
So, what I've done with not-so-pleasing results w.r.t. drawing performance upon a horizontal scroll when the number of items is more so far is this:
I disabled horizontal scrolling in my tree-view control using TVS_NOHSCROLL style (since I'm using my own scroll bar control inside the tree-view window to handle all horizontal scrolling)
I sub-classed the treeview control and in the sub-classed winproc, I handle the horizontal scroll notification and mouse notification (where I do my own hittesting and send message like TVM_EXPAND and TVM_SELECT as a result of mosue clicks/double-clicks). Also the scroll bar range is set based on how wide my custom drawn string is (the maximum length amongst all items).
I draw the string for each item upon receiving CDDS_ITEMPOSTPAINT using my own fonts for each character in the item.
The above approach (I left out some details for the sake of brevity) works BUT there are some problems which makes me post this question here and look for an alternare way:
Problems:
The horizontal scroll bar control I create is hosted "inside" the tree-view control at the bottom of the tree-view window. However, when the number of items goes beyond what the tree-view client area can accommodate vertically, the last visible tree-view item gets obscured by the scroll bar control. This can be solved by not making the scroll bar a child of the tree-view and hosting it outside the tree-view window just below it. But I don't want to do this since the scroll bar should typically be a child window of the tree-view.
This is the major one. Since I draw the items myself at each horizontal scroll, the drawing performance upon horizontal scrolling is very slow and also leads to flicker upon scrolling.
Any ideas will be much appreciated as I've been grappling with this for the last one week without success.
I can also post the relevant code here if you want to see the approach I took but I'm sure there shoould be a better approach to this and there must be some other people who would've faced this problem and solved it in the past.
Thanks in advance.
Custom-draw allows you to draw items however you want. You are not limited to a single font per item. When you receive the NM_CUSTOMDRAW notification, draw whatever you want on the provided HDC for the specified item. You can draw pieces of text in one font, pieces of text in a different font, etc. Be sure to return CDRF_SKIPDEFAULT so the TreeView itself will not try to draw anything on the item.
#Anurag S Sharma: I tried to edit this into Remy's answer. It's incomplete as is, but addresses your comment/concerns and answers this particularly vexing/useful question...
The problem is that ff I return CDRF_SKIPDEFAULT, Windows does not even draw the +/- buttons (expanding/collapsing) nor the indent lines in the control which I do want Windows to draw. – Anurag S Sharma
To retain the lines, buttons, and icons you can use ExcludeClipRect to mask only the text region and instead of returning CDRF_SKIPDEFAULT, return 0 as if you didn't draw anything. This itself would not be necessary if the text of the tree item was empty, except that the margins of the text will always be drawn by the default handler (note that Microsoft's controls do not always respect clipping shapes, but in this case they do.)
To replicate the classic TreeView label style in your custom draw procedure you need to do something like the following:
HTREEITEM item = (HTREEITEM)p->dwItemSpec;
TreeView_GetItemRect(p->hdr.hwndFrom,item,&p->rc,1);
RECT cr, rc = p->rc; GetClientRect(p->hdr.hwndFrom,&cr);
DrawTextW(p->hdc,text,-1,&rc,DT_CALCRECT|DT_NOPREFIX|DT_NOCLIP);
rc.right+=4; rc.bottom+=2; IntersectRect(&rc,&cr,&rc);
ExtTextOutW(p->hdc,rc.left+2,rc.top+1,ETO_CLIPPED|ETO_OPAQUE,&rc,text,wcslen(text),0);
I have created my own custom drawn list with checkboxesin WTL, I want to make it scrollable now, the thing is that I am subclassing a Static Text control over which I draw.. And I don't know if static controls support scrolling in any way..
Anyway my question is how do I make my custom made controll scrollable, do I have to impement the mechanism myself?
Yes, you'll have to implement it entirely by hand. That's the drawback of not using a built-in control. It probably would have been a better idea to start off with a ListBox and then customize that to your desire. That way, you would get all of the scrolling, selection, and other logic for free.
The steps are roughly as follows (there are probably ATL/WTL idioms for some or all of these, but any ATL/WTL programmer can convert back and forth from raw Win32):
Add the WS_HSCROLL and/or WS_VSCROLL window styles to your custom static control, depending on if you want a horizontal, vertical, or both scroll bars. You would add these to list of window styles passed in to the CreateWindow/CreateWindowEx function.
By default, those scroll bars won't do anything at all. You need to tell them what to do using the SetScrollInfo function. In your case:
The first parameter (hwnd) would be the handle to your control window.
The second parameter (fnBar) should be either SB_HORZ to adjust the horizontal scroll bar, or SB_VERT to adjust the vertical scroll bar.
The third parameter (lpsi) is a pointer to a SCROLLINFO structure, filled in with the desired scrolling parameters, including the current position of the thumb, the minimum and maximum values, and the "page" size used to set up the proportional scroll bar.
The fourth parameter (fRedraw) should probably be set to TRUE.
You will also need the EnableScrollBar function to enable/disable the scroll bar as appropriate. Like the previous function,
hwnd is a handle to your control window
wSBflags is either SB_HORZ, SB_VERT, or SB_BOTH
wArrows is one of the ESB_* values, depending on what you want
Finally, you will want to write code in your custom control's window procedure to handle the WM_HSCROLL and/or WM_VSCROLL messages. These are sent to the window whenever the scroll bar is moved. Inside of the handler for these messages, you will want to do the following things to update the control's state:
Call the SetScrollInfo function to update the thumb to its new position
Redraw the contents of your control in accordance with the scrolled distance. There are multiple ways of doing this, but I'd probably use the ScrollWindowEx function.
The custom control's window procedure will also need to handle the WM_SIZE message to update the scroll bar state (by calling SetScrollInfo and/or EnableScrollBar) in response to changes in the window's size.
Cody Gray provided excellent introduction into adding support for scrolling, however what you also have is the help from the WTL itself.
WTL's atlscrl.h offers you classes to inherit from and implement a custom window/control with scrolling.
// Classes in this file:
//
// CScrollImpl<T>
// CScrollWindowImpl<T, TBase, TWinTraits>
// CMapScrollImpl<T>
// CMapScrollWindowImpl<T, TBase, TWinTraits>
// CFSBWindowT<TBase>
// CZoomScrollImpl<T>
// CZoomScrollWindowImpl<T, TBase, TWinTraits>
// CScrollContainerImpl<T, TBase, TWinTraits>
// CScrollContainer
Not so much code/snippets around to demo the use, but there is still one WTL sample that covers the basics and it should be a good starting point for you as well. \Samples\BmpView has a class for scrollable bitmap:
class CBitmapView :
public CScrollWindowImpl<CBitmapView>
{
public:
You will see it's really small and it covers most of the complexity.
I have an owner drawn header of a ListView control. If the header is not owner drawn we have a nice hover effect when mouse is on one of the header's columns. After I made header columns owner drawn I have to take care of the hover effect myself.
In order to catch it I monitor WM_MOUSEMOVE message within header window and when I get this message I redraw the column of a header with changed background first and on top of that I draw text and other necessary graphics. There is just one but... The font of a text becomes bold.
You can see it below. First header is mine, and second header is from Windows explorer with hover effect on Date modified column.
Why does the font suddenly becomes bold?
Is this the right way to implement hover effect? Maybe I missed some Header specific notification?
In owner draw mode you have to take care of everything including fonts. Don't assume you know what the current DC font is. If you want a specific font you need to select one into the DC before drawing text.
Well, I solved this mystery :) Even though I still don't know why the font used in DrawThemeText function suddenly changed in WM_MOUSEMOVE notification. My approach still relies on catching WM_MOUSEMOVE event and redrawing the column underneath the mouse pointer. In order not to change the default header font (like in illustration in my question) I used the following code to get and set it:
HFONT displayFont = (HFONT)SendMessage( header, WM_GETFONT, 0, 0 ); // gets default header font
SelectObject(hdc, displayFont); // sets device context to use newly found font
This solution has at least one drawback: I redraw the Header too frequently (each time mouse moves within header column).
In order to take a screenshot of a specific window, I need to place a white colored TForm behind that window. What Windows API could I use to change the z-order of my window and place it correctly ?
Try the SetWindowPos() function.
On Delphi you can useSendToBack method, .Top and .Left properties.
form1.Top := ...;
form1.Left := ...;
form1.SendToBack;
procedure SendToBack;
Description
Use SendToBack to change the order of
overlapping controls or forms.
The order in which controls stack on
top of each other (also called the Z
order) depends on the order the
controls are placed on the form. For
example, if you put a label and an
image on a form so that one is on top
of the other, the one that was placed
first on the form becomes the one on
the bottom. Because both the label and
the image are non-windowed controls,
they "stack" as you would expect them
to. Call the SendToBack method for the
top object to move it below the other
object.
The stacking order of two windowed
controls is the same as the stacking
of two non-windowed controls. For
example, if you put a memo on a form,
then put a check box on top of it, the
check box remains on top. Calling
SendToBack for the check box makes the
memo appear on top.
The stacking order of windowed and
non-windowed controls cannot be
mingled. For example, if you put a
memo, a windowed control, on a form,
and then put a label, a non-windowed
control, on top of it, the label
disappears behind the memo. Windowed
controls always stack on top of
non-windowed controls. In this
example, calling the SendToBack method
of the memo does nothing, the label
remains behind the memo.
If the control has the input focus
when the SendToBack method executes,
it loses the input focus.
(Edit: WinSnap is a very good utility for taking and editing screenshots)
If you can get the handle of the window you want in front then I would assume that:
Pseudo Code:
MyAppWindow.BringToFront
followed by
TargetWindow.BringToFront
Should have the desired effect, yes?