How are GDI objects selected and destroyed by SelectObject function - winapi

As I am new to Visual C++, this might be a very basic question related to selecting a GDI object.
The following code snippet draws a light grey circle with no border.
cPen pen(PS_NULL, 0, (RGB(0,0,0)));
dc.SelectObject(& pen);
CBrush brush (RGB (192,192,192));
dc.SelectObject (&brush);
dc.Ellipse(0,0, 100,100);
All I understand from the code snippet is first an Object of Pen is created, and its a NULL Pen which would make the border disappear, the brush then creates a Circle of grey color, but how does dc use pen if it is already using brush? this is a bit confusing.
How does using dc.SelectObject() twice help? If the solid brush object is used to create a circle with grey color, how does creating pen object help, if it is anyway destroyed as brush object is created? how exactly does this thing work?

SelectObject function is used to select five different types of objects into DC
Pen
Brush
Font
Bitmap and
Region
The documentation states that
The newly selected object replaces the previous object of the same type. So it means you can select pen and brush without any problem but you cant select pen twice.
And moreover to avoid resource leak you need to select the old pen/brush whatever you have selected earlier
CPen pen(PS_NULL, 0, (RGB(0,0,0)));
CPen *oldPen = dc.SelectObject(& pen);
CBrush brush (RGB (192,192,192));
CBrush *oldBrush = dc.SelectObject (&brush);
dc.Ellipse(0,0, 100,100);
dc.SelectObject(oldPen);
dc.SelectObject(oldBrush);

DC object has current brush, current pen, current font etc. That is, current object of specific type. So you can select pen and brush simultaneously, but you cannot select two pens together. SelectObject internally sees the type of the handle.
Also, SelectObject returns you previous current object of the same type, which is unselected with selecting your handle you provided. You should save it and restore when you are finished.

Related

How do I force a repaint of only the background so a control doesn't repaint over it again?

This question was generated by a response to another question: Common Controls on a Transparent Window?.
Apparently, there is a way to only paint the background without a control painting itself again This would solve the problem of having common control buttons on a transparent background.
So my question is, how do I paint only the background around a common control after the common control has painted itself?
About how to redraw visible borders.
The rounded rectangle drawn by RoundRect is used as representative
The FrameRgn function draws a border around the specified region by using the specified brush.
Simple code demonstration:
HRGN hRegion = ::CreateRoundRectRgn (0, 0, ClientWidth, ClientHeight,12,12);
Canvas->Brush->Style = bsSolid;
Canvas->Brush->Color = RGB(96, 96, 96);
::FrameRgn(Canvas->Handle, hRegion, Canvas->Brush->Handle, 2, 2);
::DeleteObject(hRegion); // Don't leak a GDI object
Link you need: Redraw Border

Draw themed combobox on windows

I try to emulate the look of a themed noneditable combobox (CBS_DROPDOWNLIST) using DrawThemeBackground. I supply the part CP_READONLY, which apparently draws the background of a themed combobox:
DrawThemeBackground(theme, dc, CP_READONLY, CBRO_NORMAL, &rectangle, nullptr);
However, it does not contain the dropdown arrow. So, I tried to paint the arrow myself the following way:
rectangle.left = rectangle.right - 20;
DrawThemeBackground(theme, dc, CP_DROPDOWNBUTTONRIGHT, CBXSR_NORMAL, &rectangle, nullptr);
But the above draws the arrow centered within the rectangle on a combobox background including the border, so I cannot use this without having a border within the combobox (which itself already has a border). I used theme-explorer to verify that the arrow is always on a background with borders.
In essence, my question is: How can I draw the background and the arrow at the appropriate position to emulate the look of a plain windows combobox?
What I have found out so far:
I can specify a clipping rectangle to clip away the aforementioned borders. But this poses the question of determining the exact position rectangle and the clipping rectangle: It seems that I can use GetThemeMargins to determine the margins, but that does not tell me how large the arrow is as a whole.
GetThemeBitmap might be useful in determining the exact size of the arrow, but as I read here and confirmed on my machine, using it with TMT_GLYPHDIBDATA does not work as advertised, and I would like to go without any workarounds, if possible.

Draw inside a win32 HRGN object

I am implementing a border control in win32, and I have implemented various styles for that border. I am using simple MoveTo() and LineTo() commands for drawing the border.
My problem is, when i select some new style for my border, it starts painting over the currently drawn border. I understand i need to refresh/repaint my window inorder to have a fresh canvas. I am using InvalidateRect() for now to achieve this purpose. But I am concerned, If i have other windows associated with my border control window (as child windows), how will this effect the child windows?i will need to repaint everything on this window, correct?
Secondly, Is there anyway I can draw lines inside a GDI Region (HRGN)? So far, all i have come across is how to fill up that HRGN with some fill color. Is there anyway i can retrieve HDC associated with that particular HRGN object??
This is not a concern, anything you draw is automatically clipped by the child window rectangles. No extra code is required. The underlying window style flag is WS_CLIPCHILDREN.
You cannot draw lines in a region nor are they associated with a device context. Other than by drawing the region and then drawing the lines in your paint message handler. You are probably interested in paths. The MSDN docs start here.

Clearing a window contents in a blur behind window(DWM)

I have a window on which I call DwmExtendFrameIntoClientArea(),
the window hosts other child windows, I use Direct2D to paint on one of the child windows,
When a bitmap is loaded which has a black region on it, that region becomes blurred.
I wish to clear everything in the child window and paint it again on WM_PAINT, however I cannot seem to clear the contents.
I have tried to clear it using
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black))
This makes the child window region black, it erases all previous drawing, but no transparency.
I have tried to draw a bitmap which is just a black bar over the client area
m_pRenderTarget->DrawBitmap(m_pBkgrnd,D2D1::Rect<float> (0.f,0.f,GetWidth(),GetHeight()))
This makes whatever that had appeared before it, in black.
Tried the old GDI way of painting a black region over the entire child window,
RECT rc;
GetClientRect(m_hwnd, &rc);
HBRUSH brush = CreateSolidBrush(RGB(0,0,0));
HDC hc=GetDC(m_hwnd);
FillRect(hc, &rc, brush);
ReleaseDC(m_hwnd,hc);
DeleteObject(brush);
Doesn't work.
However if I don't do any of this and I try to resize the main window, it works right i.e. the previous painting dissappears.
Is there any API call or any way to clear the window manually before I draw it again?
How are you initializing your render target? Make sure you specify Premultipled Alpha, and not Straight or Ignore. Then, to clear everything to transparent, use ID2D1RenderTarget::Clear(D2D1::ColorF(0, 0, 0, 0)). You cannot use something like FillRectangle to draw with a transparent color, as that will blend the transparent color into what's already there and that is a no-op.

Can I create a device context that is just a portion of another device context?

I have subclassed a graphics control that takes a device context handle, HDC, as an input and uses that for drawing. My new control is just the original control centered on top of a larger image. I would like to be able to call the original control's Draw() method for code re-use, but I'm unsure how to proceed.
Here's the idea:
void CCheckBox::DrawCtrl( HDC hdc, HDC hdcTmp, LPSIZE pCtlSize, BYTE alpha ) {
// original method draws a checkbox
}
void CBorderedCheckBox::DrawCtrl( HDC hdc, HDC hdcTmp, LPSIZE pCtlSize, BYTE alpha ) {
// Draw my image here
// Create new hdc2 and hdcTemp2 which are just some portion of hdc and hdcTemp
// For example, hdc2 may just be a rectangle inside of hdc that is 20 pixels
// indented on all sides.
// Call CCheckBox::DrawCtrl() with hdc2 and hdcTemp2
}
I think you may be confused of what a device context is. A device context is a place in memory that you can draw to, be it the screen buffer or a bitmap or something else. Since I imagine you only want to draw on the screen, you only need one DC. To accomplish what you want, I would recommend passing a rectangle to the function that tells it where to draw. Optionally, and with poorer performance, you could create a new Bitmap for the smaller area, and give the function the Bitmap's DC to draw on. Now that I think about it, that might have been what you meant in the first place :P Good luck!
While not foolproof, you can fake a DC as a subsection of a DC by using a combination of SetViewportOrgEx and SelectObject with a region clipped to the sub area in question.
The problem with this approach is if drawing code already uses these APIs it needs to rewritten to be aware that it needs to combine its masking and offsetting with the existing offsets and clipping regions.

Resources