MFC Picture Control - c++

I am graphing incoming data in a picture control. Is there a direct way to change the resolution so I have a more dense grid of pixels to play with? I would rather not have to deal with any file IO because I am pressed on execution time. The dialogue around the picture control appears to be at a higher resolution.
I am sorry for the apparent lack of information that I originally provided as I am unsure exactly what vernacular to use. Below is a snippet of code that it being used in an OnPaint() function.
//Draw X Axis
int XIncrement = 40;
int TextY = YOrigin + 5;
int IntNum;
CString Str;
//Label
dc.TextOutW(10, 5, Str = "Part Profile", 12);
dc.TextOutW(GraphMax / 2, YOrigin + 20, Str = "Part Length (in)", 16);
for (int N = 0; N < 25; N++) {
IntNum = N * 5;
Str.Format(_T("%d "), IntNum);
dc.Rectangle(N * XIncrement + XOffset, YOrigin, N * XIncrement + 2 + XOffset, YOrigin + 5);
dc.TextOutW(N * XIncrement + XOffset - 7, TextY, Str, 3);
}
I needed a very basic graph to help iron out an algorithm for a bow inspection machine on a high speed manufacturing line. I used a picture control only because the MFC doesn't appear to have an elegant solution and everything else I found online seemed like a lot of overhead. I am no expert if you have some other suggestion I am open. As far as the resolution is concerned, the text around this control on my UI appears to have a more fine resolution. I was simply curious if I could control the resolution inside the picture control. This would enable me to graph the 1200 data points with more clarity.

You can't make any improvements with bitmaps or bitmap magnification. A better alternative is to give the user more control over the graph, for example insert a couple of slider controls.
One slider changes the x-offset, for example it allows the x-axis to start from zero, or to start from 50. Another slider changes the zoom. For example x-axis can be change to go from (0 to 100) or (0 to 1000)
To make the graph more interesting, enable the modern UI look by adding this line to the main *.cpp file:
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
Use a more modern font like Segoe UI, and set text background color to match the canvas background:
CFont font;
font.CreatePointFont(90, L"Segoe UI");
dc.SelectObject(font);
dc.SetBkColor(GetSysColor(COLOR_3DFACE));
dc.TextOut(0, 0, L"Hello world");

Related

Figuring out best solution for a Maze solver, with animated output

Abstract
My ultimate goal is to use Fltk to take user inputs of pixels, display a generated maze (either my own, or fetch it from the website mentioned in the details), and then show the animated solution.
This is what i've managed so far:
https://giant.gfycat.com/VioletWelloffHatchetfish.webm
Details
I'm in my first c++/algorithm class of a bachelors in CE.
As we've been learning about graphs, dijkstra etc. the last weeks i decided after watching Computerphile's video about Maze solving, to try to put the theory into "practice".
At first i wanted to output a maze from this site, http://hereandabove.com/maze/mazeorig.form.html, with the plotted solution. I chose that walls and paths should be 1x1 pixel, to make it easier to make into a 2D-vector, and then a graph.
This went well, and my program outputs a solved .png file, using dijkstra to find the shortest path.
I then wanted to put the entire solution in an animated gif.
This also works well. For each pixel it colors green/yellow, it passes an RGBA-vector to a gif-library, and in the end i end up with an animated step by step solution.
I also for each RGBA-vector passed to the gif-library, scale it up first, using this function:
//Both the buffer and resized buffer are member variables, and for each //plotted pixel in the path it updates 'buffer', and in this function makes a //larger version of it to 'resized_buffer'
// HEIGHT and WIDTH are the original size
// nHeight and nWidth are the new size.
bool Maze_IMG::resample(int nWidth, int nHeight)
{
if (buffer.size() == 0) return false;
resized_buffer.clear();
for (int i = 0; i < nWidth * nHeight * 4; i++) resized_buffer.push_back(-1);
double scaleWidth = (double)nWidth / (double)WIDTH;
double scaleHeight = (double)nHeight / (double)HEIGHT;
for (int cy = 0; cy < nHeight; cy++)
{
for (int cx = 0; cx < nWidth; cx++)
{
int pixel = (cy * (nWidth * 4)) + (cx * 4);
int nearestMatch = (((int)(cy / scaleHeight) * (WIDTH * 4)) + ((int)(cx / scaleWidth) * 4));
resized_buffer[pixel] = buffer[nearestMatch];
resized_buffer[pixel + 1] = buffer[nearestMatch + 1];
resized_buffer[pixel + 2] = buffer[nearestMatch + 2];
resized_buffer[pixel + 3] = buffer[nearestMatch + 3];
}
}
return true;
}
Problems
The problem is that it takes a looong time to do this while scaling them up, even with "small" mazes at 50x50 pixels, when trying to scale them to say 300x300. I've spent a lot of time to make code as efficient and fast as possible, but after i added the scaling, stuff that used to take 10 minutes, now takes hours.
In fltk i use the Fl_Anim_Gif-library to display animated gifs, but it wont load the maze gifs that has been scaled up (still troubleshooting this).
My real questions
Is it possible to improve the scaling function, so that it does not take forever? Or is this a totally wrong approach?
Is it a stupid idea to try to display it as a gif in fltk, would it be easier to just draw it directly in fltk, or should i rather try to display the images one after another i fltk?
I'm just familiarizing myself with fltk. Would it be easier now to use something like Qt instead. Would that be more beneficial in the long run as far as learning a GUI-library goes?
I'm mainly doing this for learning, and to start building some sort of portfolio for when i graduate. Is it beneficial at all to make a gui for this, or is this a waste of time?
Any thoughts or input would be greatly appreciated.
Whatever graphics package you use, the performance will be similar. It depends on how you handle the internals. For instance,
If you write it to a buffer and BLT it to the screen, it would be faster than writing to the screen directly.
If you only BLT on the paint event, it would be faster than forcing and update every time the screen data changes.
If you preallocate the buffers then the system does not have to keep on reallocating whenever the buffer space runs out.
Assuming that the space is preallocated, it can be written to without clearing first. Every cell it going to be written to so no need to clear, allocate and and reallocate.

Qt - Scrolling the visible area of the chart

I'm beginner at Qt, and currently learning from Mastering Qt5 book and I got stuck. There is a piece of code
// Global variables
const int CHART_X_RANGE_COUNT = 50;
const int CHART_X_RANGE_MAX = CHART_X_RANGE_COUNT - 1;
void MemoryWidget::updateSeries()
{
double memoryUsed = SysInfo::instance().memoryUsed(); // retrieve the latest memory percentage used
mSeries->append(mPointPositionX++, memoryUsed);
if (mSeries->count() > CHART_X_RANGE_COUNT)
{
QChart *chart = chartView().chart();
chart->scroll(chart->plotArea().width() / CHART_X_RANGE_MAX, 0);
mSeries->remove(0);
}
}
I only don't understand the following piece of code
chart->scroll(chart->plotArea().width() / CHART_X_RANGE_MAX, 0);
If I want to scroll to the latest point on the X axis and nothing on Y, then why i have to do this:
chart->plotArea().width() / CHART_X_RANGE_MAX
and can't do just this:
chart->scroll(1, 0);
According to docs scroll Scrolls the visible area of the chart by the distance specified by dx and dy.
and why chart->scroll(chart->plotArea().width() / CHART_X_RANGE_MAX, 0);
because mSeries->append(mPointPositionX++, memoryUsed); is called periodically and after a while a full window of xaxis will be filled and after each full window fills plot will scroll, if you use chart->scroll(1, 0); each time updateSeries() called you scroll window to next and always showing last point,
In general this plot wants to scroll per CHART_X_RANGE_MAX.

SDL2 How to position a window on a second monitor?

I am using SDL_SetWindowPosition to position my window. Can I use this function to position my window on another monitor?
UPDATE
Using SDL_GetDisplayBounds will not return the correct monitor positions when the text size is changed in Windows 10. Any ideas how to fix this?
SDL2 uses a global screen space coordinate system. Each display device has its own bounds inside this coordinate space. The following example places a window on a second display device:
// enumerate displays
int displays = SDL_GetNumVideoDisplays();
assert( displays > 1 ); // assume we have secondary monitor
// get display bounds for all displays
vector< SDL_Rect > displayBounds;
for( int i = 0; i < displays; i++ ) {
displayBounds.push_back( SDL_Rect() );
SDL_GetDisplayBounds( i, &displayBounds.back() );
}
// window of dimensions 500 * 500 offset 100 pixels on secondary monitor
int x = displayBounds[ 1 ].x + 100;
int y = displayBounds[ 1 ].y + 100;
int w = 500;
int h = 500;
// so now x and y are on secondary display
SDL_Window * window = SDL_CreateWindow( "title", x, y, w, h, FLAGS... );
Looking at the definition of SDL_WINDOWPOS_CENTERED in SDL_video.h we see it is defined as
#define SDL_WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED_DISPLAY(0)
so we could also use the macro SDL_WINDOWPOS_CENTERED_DISPLAY( n ) where n is the display index.
Update for Windows 10 - DPI scaling issue
It seems like there is indeed a bug with SDL2 and changing the DPI scale in Windows (i.e. text scale).
Here are two bug reports relevant to the problem. They are both still apparently unresolved.
https://bugzilla.libsdl.org/show_bug.cgi?id=3433
https://bugzilla.libsdl.org/show_bug.cgi?id=2713
Potential Solution
I am sure that the OP could use the WIN32 api to determine the dpi scale, for scale != 100%, and then correct the bounds by that.
DPI scaling issue ("will not return the correct monitor positions when the text size is changed")
It's a known issue with SDL2 (I encountered it in those versions: 2.0.6, 2.0.7, 2.0.8, probably the older versions have this issue as well).
Solutions:
1) Use manifest file and set there:
<dpiAware>True/PM</dpiAware>
(you need to include the manifest file to your app distribution)
2) Try SetProcessDPIAware().
Yes, you can use SetWindowPosition, if you know the boundaries of the second monitor.
You can use the function SDL_GetDisplayBounds(int displayIndex,SDL_Rect* rect) to get them.

How do I get the window geometry of top level windows excluding any decorations like shadows?

I'm using the GetWindowRect function to get the bounding rectangle of native windows. For top level windows, this seems to include any 'decorations' around the window such as the drop shadows which are enabled by default on Windows 10. I would like to exclude such decorations, but I do want to include the rest (e.g. the minimise/maximise button). How would (Should?) I adjust the geometry returned by GetWindowRect to get this?
My first attempt was to consider the window style and then use the system metrics CM_CxSIZEFRAME (error handling omitted for brevity):
[..]
RECT r;
::GetWindowRect(handle, &r);
const bool isToplevelWindow = (::GetWindowLong(handle, GWL_STYLE) & WS_CHILD) == 0;
if (isToplevelWindow) {
const int frameWidth = ::GetSystemMetrics(SM_CXSIZEFRAME);
const int frameHeight = ::GetSystemMetrics(SM_CYSIZEFRAME);
r.left += frameWidth;
r.right -= frameWidth;
r.top += frameHeight;
r.bottom -= frameHeight;
}
// process `r`
This seems to work mostly except that on the Windows 10 style, adding the frame height to the Y coordinate (i.e. r.top) appears to be wrong, it cuts out the top of the title bar.
By now, I noticed that not only does the ::GetSystemMetrics functin support querying other frame metrics:
SM_C*DLGFRAME (aka SM_CxFIXEDFRAME)
SM_CxFRAME (aka `SM_CxSIZEFRAME)
SM_CxBORDER
SM_C*PADDEDBORDER
...there is also a 'newer' GetThemeSysSize function which provides access to some further seemingly relevant metrics.
Does anybody know which of these (if any) I should respect when trying to get rid of window decorations such as rounded edges or shadows?

How to get size of check and gap in check box?

I have a check box that I want to accurately measure so I can position controls on a dialog correctly. I can easily measure the size of the text on the control - but I don't know the "official" way of calculating the size of the check box and the gap before (or after) the text.
I'm pretty sure the width of the checkbox is equal to
int x = GetSystemMetrics( SM_CXMENUCHECK );
int y = GetSystemMetrics( SM_CYMENUCHECK );
You can then work out the area inside by subtracting the following ...
int xInner = GetSystemMetrics( SM_CXEDGE );
int yInner = GetSystemMetrics( SM_CYEDGE );
I use that in my code and haven't had a problem thus far ...
Short answer:
Long Version
From MSDN Layout Specifications: Win32, we have the specifications of the dimensions of a checkbox.
It is 12 dialog units from the left edge of the control to the start of the text:
And a checkbox control is 10 dialog units tall:
Surfaces and Controls Height (DLUs) Width (DLUs)
===================== ============= ===========
Check box 10 As wide as possible (usually to the margins) to accommodate localization requirements.
First we calculate the size of a horizontal and a vertical dialog unit:
const dluCheckBoxInternalSpacing = 12; //12 horizontal dlus
const dluCheckboxHeight = 10; //10 vertical dlus
Size dialogUnits = GetAveCharSize(dc);
Integer checkboxSpacing = MulDiv(dluCheckboxSpacing, dialogUnits.Width, 4);
Integer checkboxHeight = MulDiv(dluCheckboxHeight, dialogUnits.Height, 8);
Using the handy helper function:
Size GetAveCharSize(HDC dc)
{
/*
How To Calculate Dialog Base Units with Non-System-Based Font
http://support.microsoft.com/kb/125681
*/
TEXTMETRIC tm;
GetTextMetrics(dc, ref tm);
String buffer = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
Size result;
GetTextExtentPoint32(dc, buffer, 52, out result);
result.Width = (result.X/26 + 1) / 2; //div uses trunc rounding; we want arithmetic rounding
result.Height = tm.tmHeight;
return result;
}
Now that we know how many pixels (checkboxSpacing) to add, we calculate the label size as normal:
textRect = Rect(0,0,0,0);
DrawText(dc, Caption, -1, textRect, DT_CALCRECT or DT_LEFT or DT_SINGLELINE);
chkVerification.Width = checkboxSpacing+textRect.Right;
chkVerification.Height = checkboxHeight;
Bonus Reading
What's a dialog unit?
A dialog is a unit of measure based on the user's preferred font size. A dialog unit is defined such that the average character is 4 dialog units wide by 8 dialog units high:
This means that dialog units:
change with selected font
changed with selected DPI setting
are not square
Note: Any code released into public domain. No attribution required.
Sorry for resurrecting this old thread. I recently found myself wondering about the exact same question. Currently, none of the answers above give a result consistent with Windows 10 for different fonts and font sizes, especially in high-DPI environments.
Instead, it seems that the correct result is obtained by
SIZE szCheckBox;
GetThemePartSize(hTheme, hDC, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rcBackgroundContent, TS_TRUE, &szCheckBox);
for the size of the checkbox itself. And
SIZE szZeroCharacter;
GetTextExtentPoint32(hDC, L"0", 1, &szZeroCharacter);
int iGapWidth = szZeroCharacter.cx / 2;
for the width of the gap. After trying a lot of different methods inspired by the posts above, I found L"0" in the dissembly of comctl32.dll. And while it looks like a joke to me (not necessarily a good one), I suspect it's a holdover from the old days when this might have been a good enough approximation of 2DLU.
Disclaimer: While I tested the result with various fonts and different sizes on Windows 10, I have not attempted to verify that it also holds on any other (older) version of the operating system.
It is a shame that Microsoft did not provide a way to know this for sure. I was struggling with the same question and the answer provided above is not complete. The main problem with it is that if the font of the dialog window is set to something other than the default size, that solution will not work because checkboxes will be resized.
Here's how I solved this issue (it is just an approximation that seems to have worked for me). The code is for MFC project.
1 - Create two test controls on your form, a checkbox and a radio box:
2 - Define the following custom struct:
struct CHECKBOX_DIMS{
int nWidthPx;
int nHeightPx;
int nSpacePx; //Space between checkbox and text
CHECKBOX_DIMS()
{
nWidthPx = 0;
nHeightPx = 0;
nSpacePx = 0;
}
};
3 - Call the following code when form initializes for each of the test controls (that will measure them and remove them so that end-users don't seem them):
BOOL OnInitDialog()
{
CDialog::OnInitDialog();
//Calculate the size of a checkbox & radio box
VERIFY(GetInitialCheckBoxSize(IDC_CHECK_TEST, &dimsCheckBox, TRUE));
VERIFY(GetInitialCheckBoxSize(IDC_RADIO_TEST, &dimsRadioBox, TRUE));
//Continue with form initialization ...
}
BOOL GetInitialCheckBoxSize(UINT nCtrlID, CHECKBOX_DIMS* pOutCD, BOOL bRemoveCtrl)
{
//Must be called initially to calculate the size of a checkbox/radiobox
//'nCtrlID' = control ID to measure
//'pOutCD' = if not NULL, receives the dimensitions
//'bRemoveCtrl' = TRUE to delete control
//RETURN:
// = TRUE if success
BOOL bRes = FALSE;
//Get size of a check (not exactly what we need)
int nCheckW = GetSystemMetrics(SM_CXMENUCHECK);
int nCheckH = GetSystemMetrics(SM_CYMENUCHECK);
//3D border spacer (not exactly what we need either)
int nSpacerW = GetSystemMetrics(SM_CXEDGE);
//Get test checkbox
CButton* pChkWnd = (CButton*)GetDlgItem(nCtrlID);
ASSERT(pChkWnd);
if(pChkWnd)
{
CRect rcCheckBx;
pChkWnd->GetWindowRect(&rcCheckBx);
//We need only the height
//INFO: The reason why we can't use the width is because there's
// an arbitrary text followed by a spacer...
int h = rcCheckBx.Height();
CDC* pDc = pChkWnd->GetDC();
if(pDc)
{
//Get horizontal DPI setting
int dpiX = pDc->GetDeviceCaps(LOGPIXELSX);
//Calculate
if(pOutCD)
{
//Use height as-is
pOutCD->nHeightPx = h;
//Use height for the width
pOutCD->nWidthPx = (int)(h * ((double)nCheckW / nCheckH));
//Spacer is the hardest
//INFO: Assume twice and a half the size of 3D border &
// take into account DPI setting for the window
// (It will give some extra space, but it's better than less space.)
// (This number is purely experimental.)
// (96 is Windows DPI setting for 100% resolution setting.)
pOutCD->nSpacePx = (int)(nSpacerW * 2.5 * dpiX / 96.0);
}
//Release DC
pChkWnd->ReleaseDC(pDc);
if(bRemoveCtrl)
{
//Delete window
bRes = pChkWnd->DestroyWindow();
}
else
{
//Keep the window
bRes = TRUE;
}
}
}
return bRes;
}
4 - Now you can easily resize any checkbox or radio box by calling this:
//Set checkbox size & new text
VERIFY(SetCheckBoxTextAndSize(this, IDC_CHECK_ID, &dimsCheckBox, L"New text") > 0);
//Just resize radio box
VERIFY(SetCheckBoxTextAndSize(this, IDC_RADIO_ID, &dimsRadioBox, NULL) > 0);
int SetCheckBoxTextAndSize(CWnd* pParWnd, UINT nCheckBoxID, CHECKBOX_DIMS* pDims, LPCTSTR pNewText)
{
//Set size of the checkbox/radio to 'pNewText' and update its size according to its text
//'pParWnd' = parent dialog window
//'nCheckBoxID' = control ID to resize (checkbox or radio box)
//'pDims' = pointer to the struct with checkbox/radiobox dimensions
//'pNewText' = text to set, or NULL not to change the text
//RETURN:
// = New width of the control in pixels, or
// = 0 if error
int nRes = 0;
ASSERT(pParWnd);
ASSERT(pDims);
CButton* pChkWnd = (CButton*)pParWnd->GetDlgItem(nCheckBoxID);
ASSERT(pChkWnd);
if(pChkWnd)
{
CDC* pDc = pChkWnd->GetDC();
CFont* pFont = pChkWnd->GetFont();
if(pDc)
{
if(pFont)
{
//Make logfont
LOGFONT lf = {0};
if(pFont->GetLogFont(&lf))
{
//Make new font
CFont font;
if(font.CreateFontIndirect(&lf))
{
//Get font from control
CFont* pOldFont = pDc->SelectObject(&font);
//Get text to set
CString strCheck;
if(pNewText)
{
//Use new text
strCheck = pNewText;
}
else
{
//Keep old text
pChkWnd->GetWindowText(strCheck);
}
//Calculate size
RECT rc = {0, 0, 0, 0};
::DrawText(pDc->GetSafeHdc(), strCheck, strCheck.GetLength(), &rc, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);
//Get text width
int nTextWidth = abs(rc.right - rc.left);
//See if it's valid
if(nTextWidth > 0 ||
(nTextWidth == 0 && strCheck.GetLength() == 0))
{
//Get location of checkbox
CRect rcChk;
pChkWnd->GetWindowRect(&rcChk);
pParWnd->ScreenToClient(rcChk);
//Update its size
rcChk.right = rcChk.left + pDims->nWidthPx + pDims->nSpacePx + nTextWidth;
//Use this line if you want to change the height as well
//rcChk.bottom = rcChk.top + pDims->nHeightPx;
//Move the control
pChkWnd->MoveWindow(rcChk);
//Setting new text?
if(pNewText)
{
pChkWnd->SetWindowText(pNewText);
}
//Done
nRes = abs(rcChk.right - rcChk.left);
}
//Set font back
pDc->SelectObject(pOldFont);
}
}
}
//Release DC
pChkWnd->ReleaseDC(pDc);
}
}
return nRes;
}
I'd like to give my 2 cents on the matter since iv spent an entire day working on an accurate solution for this problem that takes DPI awareness and fonts into consideration.
First define the checkbox's size in units.
#define CHECKBOX_INTERNAL_SIZE 12
Then i defined a function for converting units to pixels. NOTE: MulDiv may work just as good.
double dpi_MulDiv(double nNumber, double nNumerator, double nDenominator)
{
return (nNumber * nNumerator) / nDenominator;
}
Finally the function that does the magic. SEE the code comments for details.
//
// Get the minimum size of the Checkbox.
// NOTE: The font of the control must be set before calling this function.
//
SIZE dpi_GetCheckBoxWidth(HWND hWnd, int monitorDpi)
{
HDC dc;
HFONT hFont;
HFONT oldFont;
TEXTMETRIC tm;
double checkboxSize;
double whiteSpace;
WCHAR sourceString[128];
RECT txtRect;
SIZE size;
dc = GetDC(hWnd);
// Note that GetDC returns an uninitialized DC, which has "System" (a bitmap font) as the default font; thus the need to select a font into the DC.
hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
oldFont = (HFONT)SelectObject(dc, hFont);
// Get the Checkbox width.
checkboxSize = round(dpi_MulDiv(CHECKBOX_INTERNAL_SIZE, monitorDpi, 96));
// Get the space between the Checkbox and text.
GetTextMetrics(dc, &tm);
whiteSpace = round((double)tm.tmAveCharWidth / 2.0f);
// Get the Text width.
txtRect = { 0, 0, 0, 0 };
if (GetWindowTextW(hWnd, sourceString, 128) != 0)
{
DrawTextW(dc, sourceString, -1, &txtRect, DT_CALCRECT | DT_LEFT | DT_SINGLELINE);
}
// Cleanup.
SelectObject(dc, oldFont);
ReleaseDC(hWnd, dc);
// Done.
size.cx = (LONG)checkboxSize + (LONG)whiteSpace + txtRect.right + 3;
size.cy = ((LONG)checkboxSize < txtRect.bottom) ? txtRect.bottom : (LONG)checkboxSize;
return size;
}
I added + 3 on the last line that computes the width as a way to adjust for little irregularities. Feed back on this is welcomed. Iv only tested on Windows 10 thus far with different fonts and sizes.
This code doesn't work on Win7 with scaled UI (fonts 125% larger or 150% larger). The only thing that seems to work is:
int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96;
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
Ok dudes my way is maybe not the fastes to use in runtime, but it works for me in any case i have tested so far.
In the beginnin of my proggys i put in a function to get the size and store it in a global variable (yeah i have heard this would be bad, but i dont care about this)
here the explanation:
Create a treeview (invisible if u want)
Create an imagelist with atleast 1 image inside (size 16x16)
Set the imagelist to the treeview ("TVSIL_NORMAL")
Get the "TVSIL_STATE" imagelist from the treeview (u have to create "TVSIL_NORMAL" before, otherwise this one will fail!)
Use ImageList_GetIconSize(..) and store the size. Wow, the checkboxs and the radio-buttons have the same size as the state icons of the treeview. Now u have what u want!
Destroy the "TVSIL_NORMAL" imagelist
Destroy the treeview
this code needs only a few microseconds at the beginning of my proggies and i can use the value everytime i need it.
Preamble:
I had the same question while trying to determine the needed size of the checkbox control for a given text and found that the existing answers didn't really work for me, for several reasons:
SM_CXMENUCHECK doesn't account for the gap. In fact, I'm not convinced this is even for regular checkboxes, although it may have the same value. It may also be dependent on visual styles being enabled.
The other answers were overly complicated and felt a bit hacky (no disrespect intended, it is MS that don't make this easy).
The stated 12DLU layout was very helpful, although again feels arbitrary without a system metric to rely on.
The answers I tried still didn't yield a high enough pixel value to stop the checkbox text from wrapping.
My investigation:
I looked at how Wine reproduces the behavior and found that it also gives the same results as simply assuming 12DLU. However, the text still wrapped unless I added an extra 3 pixels to the width (even though the text should fit fine without). I also noticed that GetTextExtentPoint32 yields a value of 3 for an empty string (hmmm...)
Turning off the BS_MULTILINE style obviously stopped the text wrapping. My guess is that DrawTextW's word wrapping calculations are imperfect.
At this point I decided that the simplest solution was to just add 1 extra space to GetTextExtentPoint32, so that there would definitely be enough pixels. The over-estimate of a couple of pixels was acceptable to me.
Note that this all assumes your application is manifested as DPI aware. Otherwise I found the checkbox appeared much larger on some Windows 7 systems (not all though).
My (mostly Wine's) solution:
// This code gets the size of a piece of text and adds the size of a
// checkbox and gap. Note that this is very rough code with no error handling.
BOOL isCheckbox = TRUE;
HWND dialog = ... // Your control or dialog
HFONT font = ... // The font your control will use if it hasn't been set yet
PTCHAR text = ... // Your text
HFONT currentFont;
SIZE size;
HDC dc = GetDC(dialog);
if (!font) {
font = (HFONT)SendMessage(dialog, WM_GETFONT, 0, 0);
}
currentFont = (HFONT)SelectObject(dc, font); // NB: You should add error handling here
if (isCheckbox) {
// Or you can disable BS_MULTILINE
_tcscat(text, TEXT(" ")); // NB: This assumes text is allocated for +1 char
}
GetTextExtentPoint32(dc, text, _tcslen(text), &size); // NB: You should add error handling here
if (isCheckbox) {
int checkBoxWidth = 12 * GetDeviceCaps(dc, LOGPIXELSX ) / 96 + 1;
int checkBoxHeight = 12 * GetDeviceCaps(dc, LOGPIXELSY ) / 96 + 1;
int textOffset;
GetCharWidthW(dc, '0', '0', &textOffset);
textOffset /= 2;
size->cx += checkBoxWidth + textOffset;
if (size->cy < checkBoxHeight) {
size->cy = checkBoxHeight;
}
}
if (currentFont) {
SelectObject(dc, currentFont);
}
ReleaseDC(dialog, dc);