I'm trying to draw a custom control that should use the "combobox" theme class.
Using
m_hTheme = OpenThemeData(m_hWnd, _T("COMBOBOX"));
auto stateBG = ...; // depends on window state
DrawThemeBackground(m_hTheme, ps.hdc, CP_READONLY, stateBG, &clientRect, nullptr);
gives the correct background (read-only-look) without the chevron. But how do I add the chevron?
auto stateCV = ...; // depends on window state
DrawThemeBackground(m_hTheme, ps.hdc, CP_DROPDOWNBUTTON, stateCV, &rect, nullptr);
draws the chevron correctly, but with its own border and the chevron centered within the rect. So if I use the full client rect, I get this:
If I use a smaller rect so that the chevron is positioned correctly, I get a separated dropdown:
How do I get the "normal" look? - i.e like this:
Bonus Questions:
Is there any documentation that does a better job than MSDN? It's as sparse as most newer documentation, e.g. just listing "Parts and States", without describing their purpose (which is not always obvious), and whether it's DrawThemeBackground or ~Edgefor a particular item.
Do I still use the good old DrawFocusRectfor the focus rect?
GetThemeBackgroundContentRect calculates the expected rectable for iPartId=CP_READONLY, but for iPartId=CP_CUEBANNER, it returns the full client rectangle, so the cue text is badly aligned. Is this... normal?
Have you tried replacing CP_DROPDOWNBUTTON by CP_DROPDOWNBUTTONRIGHT ?
As a workaround you could use the ClipRect of DrawThemeBackground to cut off the left edge of the drop down button.
CRect clip_rect = rect;
clip_rect.DeflateRect(1, 0, 0, 0);
auto stateCV = ...; // depends on window state
DrawThemeBackground(m_hTheme, ps.hdc, CP_DROPDOWNBUTTON, stateCV, &rect, &clip_rect);
Related
Enabled
Disabled
When I disable a button ( Created with BS_BITMAP style flag ) it changes its look (Please see the above images), same thing happens with edit controls.
How do I make the controls not change when disabled ?
I can do that by subclassing the control, but is there an easier way ?
I don't want to subclass the controls just for that, if possible.
You do not need to subclass the control in order to do this, although I'd say it would be much cleaner. The alternative to set the BS_OWNERDRAW style and handle the WM_DRAWITEM message. That means you're taking over all drawing, but that's okay since you don't want it to look like a normal button anyway.
I could not agree more with Jonathan Potter's observation that it is extremely bad UI design to fail to indicate to the user which buttons are enabled and which ones are not. There are multiple ways to do this, but not doing it is not a viable option. Fortunately, it is easy to do with WM_DRAWITEM, since it tells you the button's current state.
So make the WM_DRAWITEM message handler look like this (in the parent window's window procedure):
case WM_DRAWITEM:
{
const DRAWITEMSTRUCT* pDIS = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
// See if this is the button we want to paint.
// You can either check the control ID, like I've done here,
// or check against the window handle (pDIS->hwndItem).
if (pDIS->CtlID == 1)
{
// Load the bitmap.
const HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
// Draw the bitmap to the button.
bool isEnabled = (pDIS->itemState & ODS_DISABLED) == 0;
DrawState(pDIS->hDC,
nullptr,
nullptr,
reinterpret_cast<LPARAM>(hBmp),
0, 0, 0, 0, 0,
DST_BITMAP | (isEnabled ? DSS_NORMAL : DSS_DISABLED));
// Delete the bitmap.
DeleteObject(hBmp);
// Draw the focus rectangle, if applicable.
if ((pDIS->itemState & ODS_FOCUS) && ((pDIS->itemState & ODS_NOFOCUSRECT) == 0))
{
DrawFocusRect(pDIS->hDC, &pDIS->rcItem);
}
// Indicate that we handled this message.
return TRUE;
}
break;
}
Naturally, you could optimize this code further by loading the bitmap a single time and caching it in a global object, rather than loading and destroying it each time the button needs painting.
Note that I've used the DrawState function, which can draw bitmaps either in a "normal" (DSS_NORMAL) or "disabled" (DSS_DISABLED) state. That simplifies the code considerably, and allows us to easily handle the disabled state, but unfortunately the result looks a little bit ugly. That's because the DrawState function converts the bitmap to monochrome before applying any effects other than normal.
You probably don't like that effect, so you'll need to do something else. For example, use two separate images, one for the enabled state and the other for the disabled state, and draw the appropriate one. Or convert your normal color image into grayscale, then draw that for the disabled state.
And if the custom-drawing code runs too slowly, you can optimize it even further by checking the value of pDIS->itemAction and only re-drawing the necessary portions.
Then, once you think you've got everything all polished and efficient, the inevitable bug reports will start to roll in. For example, keyboard accelerators are not supported. Then, once you add support for these, you'll need to indicate that in the UI. That will be difficult with a bitmap that already contains the text; the only way to draw a letter underlined is to draw the text yourself. This all proves that owner-draw is way too much work. Just let Windows draw the controls the normal way, don't break everything for your users just because some designer thinks it "looks cool".
I am trying to get my dialog box to match. I have been all through google, random testing, etc, even read some places it cant be done.
What I have been able to do is to use one of the messages to set font and colors, but nowhere about drawing itself.
I would think it has to be able to do...
Does anyone have any ideas? Or know anything about this?
http://imageshack.com/a/img832/5955/91m.png
It looks like edit controls don't support owner draw, but you can still solve your direct problem. According to the MSDN page for EDITTEXT, by default edit controls in a resource file have the WS_BORDER style set. Looks like you can get rid of it with something like this:
EDITTEXT IDC_EDIT1,17,51,136,14,ES_AUTOHSCROLL | NOT WS_BORDER
For the status bar, you might try using a static control with customized colors instead of a real status bar. Or you could roll your own, specify the window class name in the resource file, and make sure you register the class before displaying the dialog.
UPDATED: Wow, the documentation for status bar is terrible. You can owner draw one, though. Follow these steps:
// where hStatus is the HWND of a status bar...
// You must set simple mode to false, because simple mode doesn't
// support owner draw.
SendMessage(hStatus, SB_SIMPLE, FALSE, 0);
// I'm assuming 1 status bar part for demonstration. Setting the right edge
// for the 1 part to -1 make it take up the whole status bar.
int partWidths[] = { -1 };
SendMessage(hStatus, SB_PARTS, 1, reinterpret_cast<LPARAM>(partWidths));
// There is background stuff that stays behind even with owner draw,
// so you have to set the background color to black, too, to get rid of
// any appearance of borders.
SendMessage(hStatus, SB_SETBKCOLOR, 0, RGB(0, 0, 0));
// There is still a slim border that stays behind, so you need to set
// SBT_NOBORDERS in addition to SBT_OWNERDRAW. The 0 is the index of the
// status bar part. It could be anything between 0 and 255.
SendMessage(
hStatus,
SB_SETTEXT,
SBT_NOBORDERS | SBT_OWNERDRAW | 0,
reinterpret_cast<LPARAM>(_T("Status")));
From there, you must also handle the WM_DRAWITEM for the status bar. Now, as to why I say the documentation for status bar is terrible...
Docs for SB_SETTEXT say the high byte of the low order word of the WPARAM can be one of the values that follows. There are two problems with this:
You can combine them, and you must for this to work. MFC does it, too. I checked.
You might be tempted to write MAKEWPARAM(MAKEWORD(0, SBT_OWNERDRAW), 0). This will not work. By appearances, the SBT_ styles are defined so that they will automatically appear in the high byte of the low word if you just OR them with your index value.
That I had to look at the MFC source code to figure out how to use SB_SETTEXT correctly is telling.
Edit controls do not have an owner-draw mode, however you can subclass an Edit control and process messages like WM_ERASEBKGND, WM_NCPAINT, WM_PAINT, etc, as well as the WM_CTLCOLOREDIT message sent to the edit's parent window.
The answer for part 2, vertical aligning text in an edit:
RECT rect;
GetClientRect(GetDlgItem(hwnd, IDC_TIMEINPUT),&rect);
Rectangle(hdcEdit, rect.left, rect.top, rect.right, rect.bottom);
rect.left+=5; rect.top+=5; rect.right+=5; //rect.bottom+=5;
SendMessage(GetDlgItem(hwnd, IDC_TIMEINPUT), EM_SETRECTNP, 0, (LPARAM)&rect);
Has to be multi-line, and you really do have to play around with different numbers to keep it single lined, and maintain the vertical align. The EMS_SETRECTNP allows you to specify where you want the text to be, allowing the Edit to have a larger height.
I have been using directx for a while now and one thing that has always bothered me is that windows are squares (I guess this applies to most programs). Now as creation often happens by defining a rectangle shape and drawing that, black for example. I have been thinking of 2 approaches to this:
Define a bigger rectangle and draw parts of the background transparent.
I decided not to go for this one as I have absolutely no idea how to do this.
See what microsoft offers when it comes to window shapes.
And while they did have a lot of win32 configuration settings (no border etc) I couldn't find anything about drawing in a particular shape (like using a triangle for example).
Does anyone have experience with window shapes or drawing a background transparent? Maybe even a better option that I missed? Thanks in advance!
This can be done quite simply using the SetWindowRgn API call. What this does is define the area within which the system will allow drawing to appear.
As a simple example, lets punch a hole in one of our windows. The following can be done in the WM_CREATE handler of the window:
case WM_CREATE:
{
// Get the window rect
RECT r;
GetWindowRect(m_hwnd, &r);
MapWindowPoints(NULL, m_hwnd, reinterpret_cast<LPPOINT>(&r), 2);
// Work out the size of the window
LONG w = r.right - r.left;
LONG h = r.bottom - r.top;
// Create a rectangular region to cover the window (almost)
HRGN hRgn = CreateRectRgnIndirect(&r);
// and a smaller elliptical window
r.left += w/4;
r.right -= w/4;
r.top += h/4;
r.bottom -= h/4;
HRGN rgnCirc = CreateEllipticRgnIndirect(&r);
// Now we combine the two regions, using XOR to create a hole
int cres = CombineRgn(hRgn, rgnCirc, hRgn, RGN_XOR);
// And set the region.
SetWindowRgn(m_hwnd, hRgn, TRUE);
}
break;
Some important notes. The region that is passed to SetWindowRgn is from that point on owned by the system, so do not perform any more operations on it. Also, you would need to modify the region if the window is resized - I only put the example in WM_CREATE as... an example.
Another little caveat about the above, it doesn't perform the calculation of window size correctly... as I said, this is just an example that punches a hole in a window.
Finally, I tried it with a simple Direct-X program, and it works with that too. Hoorah!
I have a GtkDrawingArea that is used to visualize data.
Depending on the database and user input the drawing can grow really large (larger than the maximum allowed GtkDrawingArea size). Therefore I would like to use a drawing area that is just as big as the current window and update it manually upon scrolling.
If I use the ScrolledWindow + Viewport method to add scroll-bars to the drawing area it does obviously not work because the drawing area is not big enough to need scroll-bars.
Is there any way that that I can trick the viewport into thinking that the underlying widget is larger than it actually is?
If not what would be the best way to solve this problem?
Note: I am using Gtk2 and switching to Gtk3 is not a possibility.
You need to subclass GtkDrawingArea and override the set_scroll_adjustments signal. GtkWidget docs
In this signal you will get the adjustments for the scrolled window. I wrote some code a few years back that you can look at to see how to implement it.
MarlinSampleView code
This code was able to pretend that the widget was millions of pixels wide when in reality it wasn't any bigger than the window.
It turned out to be quite simple:
The GtkScrolledWindow has another constructor which can be used to set the GtkAdjustments that the scrolled window should use.
//These adjustments will be attached to the scrollbars.
prvt->hAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
prvt->vAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
GtkWidget* scrolledTree = gtk_scrolled_window_new(prvt->hAdjustment, prvt->vAdjustment);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledTree), drawing_area);
Now, whenever the size of the drawing changes you just need to modify the GTKAdjustments to simulate the change. There is no need to actually resize the drawing area.
gtk_adjustment_set_lower(prvt->hAdjustment, 0);
gtk_adjustment_set_step_increment(prvt->hAdjustment, 1);
gtk_adjustment_set_page_increment(prvt->hAdjustment, 10);
gtk_adjustment_set_upper(prvt->hAdjustment, picture_width);
gtk_adjustment_set_page_size(prvt->hAdjustment, scrollArea_width);
gtk_adjustment_changed(prvt->hAdjustment);
Notice that I call gtk_adjustment_changed in the end. This is important, otherwise the ScrolledWindow will not update the scrollbars.
Finaly the value_changed callback of the GtkAdjustmens can be used to catch the scroll events and adjust the drawing.
Edit: This does not work properly because the GtkScrolledWindow receives the scroll event
as well and moves the image :(
I am just starting with MFC so please be tolerant ;).
I have wrote (it was mostly generated to be honest) a simple application which should do the Paint chores: drawing lines, rectangulars, ellipses, changing a color of object to be drawn etc.
I need to save what has been drawn on the screen into a bmp file. Any ideas how can I achieve this ?
I do not know if that's relevant but I am drawing objects on the screen without the use of any CBitmaps or things like that. Here is a part of code responsible for drawing :
CPaintDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
Anchor.x=point.x;
Anchor.y=point.y;
OldPoint.x=Anchor.x;
OldPoint.y=Anchor.y;
if(pDoc->shapeCount>=MAX_SHAPES) return;
pDoc->shapeCount++;
if(bFreehand)
{
pDoc->m_shape[pDoc->shapeCount-1] = new Shape;
pDoc->m_shape[pDoc->shapeCount-1]->shape = ePoint;
}
if(bLine)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CLine;
pDoc->m_shape[pDoc->shapeCount-1]->shape = eLine;
}
if(bRectangle)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CRectangle;
pDoc->m_shape[pDoc->shapeCount-1]->shape = eRectangle;
}
if(bEllipse)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CEllipse;
pDoc->m_shape[pDoc->shapeCount-1]->shape=eEllipse;
}
pDoc->m_shape[pDoc->shapeCount-1]->x=point.x;
pDoc->m_shape[pDoc->shapeCount-1]->y=point.y;
pDoc->m_shape[pDoc->shapeCount-1]->x2=point.x;
pDoc->m_shape[pDoc->shapeCount-1]->y2=point.y;
pDoc->m_shape[pDoc->shapeCount-1]->Pen=CurrentPen;
pDoc->m_shape[pDoc->shapeCount-1]->Brush=CurrentBrush;
bButtonDown=true;
SetCapture();
I have found this way to do it but I don't know how to obtain screen width and height to fill it in the CreateBitmap parameter's list
CBitmap *bitmap;
bitmap.CreateBitmap(desktopW, desktopH, 1, 32, rgbData);
CImage image;
image.Attach(bitmap);
image.Save(_T("C:\\test.bmp"), Gdiplus::ImageFormatBMP);
The CreateBitmap call only requires the desktop width and height if the image you wish to save is actually the entire size of the screen. If that's indeed your intent, you can use CWnd::GetDesktopWindow() to get a CWnd object that you can query for its width and height:
http://msdn.microsoft.com/en-us/library/bkxb36k8(v=VS.80).aspx
That gets dodgy in general...if for no other reason than multi-monitor scenarios...so I'd recommend against it unless you really feel like writing a screen capture app.
What you probably want to do isn't to take a full screen shot, but just save the contents of your program's window. Typically you'd do this by breaking out the drawing logic of your program so that in the paint method you call a helper function that is written to take a CDC device context. Then you can either call that function on the window-based DC you get in the paint call or on a DC you create from the bitmap to do your save. Note that you can use a CBitmap in CDC::SelectObject:
http://msdn.microsoft.com/en-us/library/432f18e2(v=VS.71).aspx
(Though let me pitch you on not using MFC. Try Qt instead. Way better.)