How to paint transparent areas for child controls? - windows

I have a CTabCtrl subclass which I'm trying overriding WM_PAINT to perform custom drawing. The only problem is, when I change the selected tab, I get artifacts left on the dialog where the old paint code hasn't been erased before the new code is painted on top. (The standard tab controls have the selected tab appear 2 pixels bigger than non-selected tabs, so when you change from selected to non-selected, you are left with the previous paint artifacts).
What is the best way of "repainting" this area? I've tried getting the parent control's DC and BitBlt'ing that onto the child's DC, but that doesn't work because the parent DC already contains an image of this control.

Do you override the WM_ERASEBACKGROUND message as well? You should probably do that and erase the area in the control.
You could use DrawThemeParentBackground to draw the background (XP and later) if you don't want to replace all the drawing logic.

Related

Treeview control in Win32 API - how to display a single treeview item using different fonts?

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);

Weird black pixels in the corners of CProgressCtrl controls

I have an MFC dialog (actual a dialog bar with in a dialog) that contains a progress bar with these strange black pixels in the corner. I have tried the following to remove them:
Change most of the border type styles and the transparent style of the control.
Override the OnEraseBkgnd in a class derived a CProgressCtrl.
Setting the background color of the control by PBM_SETBKCOLOR.
I have yet to find a way to remove these black pixel.
Here is an example of what it looks like:
It seems I have found the issue. When the progress bar performs its painting it was sending an WM_ERASEBKGND message to the dialog to get the background with which it would paint. Some how the DC brush origin was being messed up. Using the following code at the start of the function seems to have fixed the issue with no ill effects.
CRect rcClip;
pDC->GetClipBox(rcClip);
pDC->SetBrushOrg(-rcClip.left, -rcClip.top);
I've seen this occur when using ActiveX controls inside control containers that don't have a a window or proper Device Contexts (The VB6 frame control is one of these) but I'm not sure if the same problem applies to MFC windows and controls though.
Try making the control parent a normal static window.

Setting transparent background Win32

What I'm trying to do is very simple but there doesn’t seem to be a lot of information on it. Basically I have a normal non-transparent parent window and I want to place a child window (that has its own non-transparent controls) inside that parent window. So the only thing I have to do is to set the background brush of the child window transparent, but it still draws a white background. Tried using WS_EX_LAYERED style and SetLayeredWindowAttributes but it makes the child window invisible.
There are two basic ways to achieve "transparent" child controls on Windows:
Handle WM_CTLCOLORxxx messages in the parent window.
This is a convenient way to make existing controls support transparency. Each control will send a WM_CTLCOLORxxx message appropriate to the type of the control. If you create a brush that represents the background skin of the dialog, and return that from each message, the net effect will be as if each control was painted with a transparent outer area.
This fails however if you want alpha effects, or for controls to physically overlap each other.
Use WS_EX_COMPOSITED on the parent window.
Without this style, the window managers paint order of child windows is undefined, in practice, its top to bottom. When trying to alpha blend overlapping controls the result will be... unpleasant. WS_EX_COMPOSITED will ensure the child windows are painted bottom to top. Make sure that you DON'T use either the WS_CLIPCHILDREN or WS_CLIPSIBLINGS styles as that will prevent the overlapping areas from being painted at all.
You still need to do something clever via WM_CTLCOLORxxx messages as the standard controls are still going to try and fill their entire rect with dialog-background-grey.

Correct way to do transparent buttons in WINAPI

How do you make the button with overrided WM_PAINT transparent. So that you could see the form through it in all places except where something is drawn. At the same time avoiding the flicker.
I saw examples with SetBkMode(HDC, TRANSPARENT), using NULL_BRUSH, examples with CompatibleDC and BitBlts, but I'm not quite sure which is the correct way to go, and also, how it behaves when WM_CLIPCHILDREN is set on the parent window. Most of the experiments I did had some weird behavior as well. Can't find a good documentation on WM_ERASEBKGND/WM_CTLCOLOR/WM_PAINT/WS_EX_COMPOSITED/WS_CLIPCHILDREN/etc internal relations on MSDN at all.
Does anyone know where I could read about this topic with all the gotchas associated?
mmm, I've never found anything close to a authoritative document on this topic.
This just serves as my rather random memory dump trying to get controls to "play nice", when animated, on a window that was either skinned (normal non client area with a bitmap background), layered (to get a window with custom non client edges with drop shadow effects) or with extended Aero Glass (via the DwmExtendFrameIntoClient) effects.
SetBKMode(... TRANSPARENT) just ensures that text rendering does not fill in the background of the text with the current bk color.
WS_EX_COMPOSITED causes windows to paint the parent and all child windows to a back buffer when the parent is invalidated, and then paint the back buffer. This is flicker free, but NT 6.0 introduced the desktop window manager which does not honor WS_EX_COMPOSITED.
WS_CLIPCHILDREN prevents the child windows and the parent window painting the same area twice. But is contra indicated if you need to use group boxes, or tab controls.
WS_CLIPSIBLINGs could be useful if child windows overlap and cause flicker. again this style is useless if you need to use group boxes or tab controls.
The other problem with WS_CLIPCHILDREN is you can't paint a background in the parent widnows window proc and rely on a NULL brush to reveal the skin. You can return a brush from WM_CTLCOLORxxx messages to coerce some of the standard controls to paint their background with your skin bitmap.
WS_EX_LAYERED is another style that makes windows buffer the painting of your parent window. But the layered windows painter does not paint child windows at all, so you need to manually paint the child windows by sending WM_PRINTCLIENT messages. Not all controls support this message.
WPF gets around the lack of back buffered painting and alpha support by not creating actual child windows at all for its buttons.
Final take on the situation:
With a little work you can get a skin behind most standard controls easily. WS_CLIPCHILDREN and no background painting on the parent will mimimize flicker. Handle WM_CTLCOLORxxx to fill the background on the controls.
If you are using Group Boxes or TabControls to frame other controls you absolutely must get the Z-order correct if using WS_CLIPSIBLINGS.
By sending controls WM_PRINTCLIENT messages, and some subclassing, you can get standard controls to paint onto a DIBSection, which you can then manually (or use DWM worker functions) repair the alpha channel of, and then paint onto a layered window, or a window with extended aero glass. This is even flicker free, but controls that don't support WM_PRINTCLIENT, or frequently update themselves outside of WM_PAINT, will not display correctly.

How can I change the way NSButtonCell objects highlight when clicked?

I am using several NSButtonCell objects in an NSTableView. They are simple square buttons with custom images on them.
These buttons draw properly when they are not highlighted: all that is visible is the image, and the rest of the button rectangle is transparent. However, when I click on them, the entire button rectangle is highlighted, inverting the background in the parts that were transparent.
I would prefer to see the image drawn inverted, and the transparent parts remain transparent. How can this be done?
Try setting your cell's highlightsBy property to NSContentsCellMask. I think you'll have to do this in code (probably in awakeFromNib); I don't see a way to do it in IB alone.
You can do it in Interface Builder too. I use "Square Button" so the button alters between two images (so the image is not inverted at all).
Your buttons behaviour is probably set to "Momentary Light" or "Momentary Push In".
Set the Behaviour to "Momentary Change", and it should work.

Resources