Related
I am trying to find a way to show tooltip even if we disable window like this:
EnableWindow(hWnd, false);
Here is how I am creating tooltip:
void CreateTooltip(HWND hparent)
{
HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 0, 0, 0, 0, hparent, NULL, hInstance, NULL);
TTTOOLINFO ti = { 0 };
//ti.cbSize = sizeof(TTTOOLINFO);
//*********************************************************
// Specific settings for specific compiler options (Unicode/VC2013)
//*********************************************************
ti.cbSize = TTTOOLINFOW_V2_SIZE;
ti.uFlags = TTF_SUBCLASS;
ti.hwnd = hparent;
ti.lpszText = (LPWSTR)(L"Tooltip string");
GetClientRect(hparent, &ti.rect);
if (!SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti))
MessageBox(0, TEXT("Failed: TTM_ADDTOOL"), 0, 0);
}
I want to have two tooltips, one for enabled state and one for disabled.
Maybe there is a way to avoid this limitation? Thanks in advance.
I want to insert buttons of diffrent sizes in a toolbar.
Lets say for example 2 buttons. One have a size of 32x16, the Other 16x16.
I used Two Image Lists. Each image list has it's own image size (1st 32x16 - 2nd 16x16).
But the problem is, when I compile the code i get the images loaded correctly but the buttons have the same width(32).
code:
//(1) Create the Toolbar ImageList
HIMAGELIST hImageListLarge = ImageList_Create(32, 16, ILC_COLOR8 | ILC_MASK, 1, 0);
if (!hImageListLarge)
return false;
HIMAGELIST hImageListSmall = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 1, 0);
if (!hImageListSmall)
return false;
//(2) Fill the Image List
if (ImageList_Add(hImageListLarge,
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_CONNECT_TO)),
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_CONNECT_TO_MASK))) == -1)
return false;
if (ImageList_Add(hImageListSmall,
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_HELP)),
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_HELP_MASK))) == -1)
return false;
//(3) Create the Toolbar window
m_hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, nullptr,
WS_CHILD | TBSTYLE_WRAPABLE | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | TBSTYLE_TOOLTIPS ,
0, 0, 0, 0, m_hWnd, nullptr, m_hInstance, nullptr);
if (m_hToolbar == nullptr)
return false;
//(4) Sets the Image list for the Toolbar
SendMessage(m_hToolbar, CCM_SETVERSION, 5, 0);
SendMessage(m_hToolbar, TB_SETIMAGELIST, 0, (LPARAM)hImageListLarge);
SendMessage(m_hToolbar, TB_SETIMAGELIST, 1, (LPARAM)hImageListSmall);
//(5) Initialize the TBBUTTON structures for each button
m_tbButtons[0].fsStyle = BTNS_SEP;
m_tbButtons[1].iBitmap = MAKELONG(0, 0);
m_tbButtons[1].idCommand = ID_CONNECTIONS_CONNECT_TO;
m_tbButtons[1].fsState = TBSTATE_ENABLED;
m_tbButtons[1].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE;
m_tbButtons[1].iString = (INT_PTR)TEXT("Connect to");
m_tbButtons[2].iBitmap = MAKELONG(0, 1);
m_tbButtons[2].idCommand = ID_HELP_ABOUT;
m_tbButtons[2].fsState = TBSTATE_ENABLED;
m_tbButtons[2].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE ;
m_tbButtons[2].iString = (INT_PTR)TEXT("Help");
//(6) Add buttons to the toolbar
SendMessage(m_hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(m_hToolbar, TB_ADDBUTTONS, 3, (LPARAM)m_tbButtons);
//(7) auto Resize the toolbar
SendMessage(m_hToolbar, TB_AUTOSIZE, 0, 0);
SendMessage(m_hToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_MIXEDBUTTONS);
ShowWindow(m_hToolbar, SW_SHOW);
//(8) Modify the size of the separator that will hold the combobox
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
tbbi.cx = 500;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)0, (LPARAM)& tbbi);
tbbi.cx = 32;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)1, (LPARAM)& tbbi);
tbbi.cx = 16;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)2, (LPARAM)& tbbi);
//(9) Get the Rectangle occupied by the separator
RECT rcSep;
SendMessage(m_hToolbar, TB_GETITEMRECT, (WPARAM)0, (LPARAM)& rcSep);
//(10) Create the hosts comboBox
m_hComboHosts = CreateWindow(WC_COMBOBOXW, nullptr, WS_CHILD | CBS_DROPDOWN | WS_VISIBLE,
rcSep.left, rcSep.top, rcSep.right - rcSep.left, rcSep.bottom - rcSep.top, m_hToolbar,
(HMENU)IDC_COMBO_HOSTS, m_hInstance, nullptr);
if (!m_hComboHosts)
return false;
return true;
After a quick check, please modify the following code:
Comment this line of codeļ¼
SendMessage(m_hToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_MIXEDBUTTONS);
TBSTYLE_EX_MIXEDBUTTONS
Version 5.81. This style allows you to set text for all buttons, but
only display it for those buttons with the BTNS_SHOWTEXT button style.
The TBSTYLE_LIST style must also be set. Normally, when a button does
not display text, your application must handle TBN_GETINFOTIP or
TTN_GETDISPINFO to display a tooltip. With the TBSTYLE_EX_MIXEDBUTTONS
extended style, text that is set but not displayed on a button will
automatically be used as the button's tooltip text. Your application
only needs to handle TBN_GETINFOTIP or or TTN_GETDISPINFO if it needs
more flexibility in specifying the tooltip text.
When you set TBSTYLE_EX_MIXEDBUTTONS style, it will allow you to set text for all buttons.
After testing, this style will prevent you from changing the width of the button.
If you want to display the button text, just add the BTNS_SHOWTEXT style.
Modified code:
//(1) Create the Toolbar ImageList
HIMAGELIST hImageListLarge = ImageList_Create(32, 16, ILC_COLOR8 | ILC_MASK, 1, 0);
if (!hImageListLarge)
return false;
HIMAGELIST hImageListSmall = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 1, 0);
if (!hImageListSmall)
return false;
//(2) Fill the Image List
if (ImageList_Add(hImageListLarge,
LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TB_CONNECT_TO)),
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_CONNECT_TO_MASK))) == -1)
return false;
if (ImageList_Add(hImageListSmall,
LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TB_HELP)),
LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_TB_HELP_MASK))) == -1)
return false;
//(3) Create the Toolbar window
m_hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, nullptr,
WS_CHILD | TBSTYLE_WRAPABLE | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | TBSTYLE_TOOLTIPS,
0, 0, 0, 0, m_hWnd, nullptr, hInst, nullptr);
if (m_hToolbar == nullptr)
return false;
//(4) Sets the Image list for the Toolbar
SendMessage(m_hToolbar, CCM_SETVERSION, 5, 0);
SendMessage(m_hToolbar, TB_SETIMAGELIST, 0, (LPARAM)hImageListLarge);
SendMessage(m_hToolbar, TB_SETIMAGELIST, 1, (LPARAM)hImageListSmall);
//(5) Initialize the TBBUTTON structures for each button
m_tbButtons[0].fsStyle = BTNS_SEP;
m_tbButtons[1].iBitmap = MAKELONG(0, 0);
m_tbButtons[1].idCommand = ID_CONNECTIONS_CONNECT_TO;
m_tbButtons[1].fsState = TBSTATE_ENABLED;
m_tbButtons[1].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; // If you want display text, you can add BTNS_SHOWTEXT
m_tbButtons[1].iString = (INT_PTR)TEXT("Connect to");
m_tbButtons[2].iBitmap = MAKELONG(0, 1);
m_tbButtons[2].idCommand = ID_HELP_ABOUT;
m_tbButtons[2].fsState = TBSTATE_ENABLED;
m_tbButtons[2].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE;
m_tbButtons[2].iString = (INT_PTR)TEXT("Help");
//(6) Add buttons to the toolbar
SendMessage(m_hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(m_hToolbar, TB_ADDBUTTONS, 3, (LPARAM)m_tbButtons);
//(7) auto Resize the toolbar
SendMessage(m_hToolbar, TB_AUTOSIZE, 0, 0);
//SendMessage(m_hToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_MIXEDBUTTONS);
ShowWindow(m_hToolbar, SW_SHOW);
//(8) Modify the size of the separator that will hold the combobox
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
tbbi.cx = 44;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)1, (LPARAM)& tbbi);
tbbi.cx = 27;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)2, (LPARAM)& tbbi);
//(9) Get the Rectangle occupied by the separator
RECT rcSep;
SendMessage(m_hToolbar, TB_GETITEMRECT, (WPARAM)0, (LPARAM)& rcSep);
//(10) Create the hosts comboBox
m_hComboHosts = CreateWindow(WC_COMBOBOXW, nullptr, WS_CHILD | CBS_DROPDOWN | WS_VISIBLE,
rcSep.left, rcSep.top, rcSep.right - rcSep.left, rcSep.bottom - rcSep.top, m_hToolbar,
(HMENU)IDC_COMBO_HOSTS, hInst, nullptr);
if (!m_hComboHosts)
return false;
return true;
I used the first TB_SETBUTTONINFO message to modify the size of a button (Separator).
To make it large enough for holding a comboBox control:
//(8) Modify the size of the separator that will hold the combobox
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
tbbi.cx = 500;
SendMessage(m_hToolbar, TB_SETBUTTONINFO, (WPARAM)0, (LPARAM)& tbbi);
This is the code for creating the ComboBox:
//(9) Get the Rectangle occupied by the separator
RECT rcSep;
SendMessage(m_hToolbar, TB_GETITEMRECT, (WPARAM)0, (LPARAM)& rcSep);
//(10) Create the hosts comboBox
m_hComboHosts = CreateWindow(WC_COMBOBOXW, nullptr, WS_CHILD | CBS_DROPDOWN | WS_VISIBLE,
rcSep.left, rcSep.top, rcSep.right - rcSep.left, rcSep.bottom - rcSep.top, m_hToolbar,
(HMENU)IDC_COMBO_HOSTS, m_hInstance, nullptr);
This is working as expected.
But when I try to add two other buttons (32x16 - 16x16), I get always the same width of the first Imagelist (32).
But when I swap the image list indexes, I get (16 for both).
I am using the new Visual styles ComCtl32.lib
I am at the design stage here.
Code:
if (m_ToolBar.CreateEx(this, TBSTYLE_TRANSPARENT | TBSTYLE_FLAT,
AFX_DEFAULT_TOOLBAR_STYLE, CRect(1, 1, 1, 1), IDR_TOOLBAR))
{
m_ToolBar.SetPaneStyle(m_ToolBar.GetPaneStyle()
& ~(CBRS_GRIPPER | CBRS_SIZE_DYNAMIC | CBRS_BORDER_ANY));
m_ToolBar.SetBorders();
m_ToolBar.LoadBitmap(IDB_BMP_SM_IMAGELIST);
m_ToolBar.InsertButton(CMFCToolBarButton(ID_FILE_OPEN_CHRISTIAN_LIFE_AND_MINISTRY_REPORT, 0, _T("Open MWB"), TRUE));
m_ToolBar.InsertButton(CMFCToolBarButton(ID_FILE_CREATE_CHRISTIAN_LIFE_AND_MINISTRY_REPORT, 0, _T("Create MWB"), TRUE));
m_ToolBar.InsertSeparator(2);
m_ToolBar.InsertButton(CMFCToolBarButton(ID_FILE_OPENREPORT, 0, _T("Open SRR"), TRUE));
m_ToolBar.InsertButton(CMFCToolBarButton(ID_FILE_CREATEREPORT, 0, _T("Create SRR"), TRUE));
m_ToolBar.InsertSeparator(5);
m_ToolBar.InsertButton(CMFCToolBarButton(ID_OPTIONS_PUBLISHERS_DATABASE, 0, _T("Publishers Database"), TRUE));
CSize sizeToolBar = m_ToolBar.CalcFixedLayout(FALSE, TRUE);
m_ToolBar.SetWindowPos(NULL, 0, 10, sizeToolBar.cx, sizeToolBar.cy,
SWP_NOACTIVATE | SWP_NOZORDER);
}
I call the above in OnInitDialog of my CDialog.
Why is the toolbar overlapped:
Update
I simplified the code to just use a basic toolbar resource:
if (m_ToolBar.CreateEx(this, TBSTYLE_TRANSPARENT | TBSTYLE_FLAT,
AFX_DEFAULT_TOOLBAR_STYLE, CRect(1, 1, 1, 1), IDR_TOOLBAR))
{
m_ToolBar.SetPaneStyle(m_ToolBar.GetPaneStyle()
& ~(CBRS_GRIPPER | CBRS_SIZE_DYNAMIC | CBRS_BORDER_ANY));
m_ToolBar.SetBorders();
m_ToolBar.LoadToolBar(IDR_TOOLBAR);
CSize sizeToolBar = m_ToolBar.CalcFixedLayout(TRUE, TRUE);
m_ToolBar.SetWindowPos(NULL, 0, 10, sizeToolBar.cx, sizeToolBar.cy,
SWP_NOACTIVATE | SWP_NOZORDER);
}
But the results are still the same.
Update
I noticed if I just resize my dialog in the IDE that I then have room for the toolbar. But how to do I accurately resize it in the IDE to allow for a 32 pixel high toolbar?
Someone provided a link to this archived article.
This is my updated code (called from OnInitDialog):
void CMeetingScheduleAssistantDlg::CreateToolbar()
{
DWORD dwCtrlStyle = TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CBRS_SIZE_DYNAMIC;
DWORD dwStyle = AFX_DEFAULT_TOOLBAR_STYLE;
if (m_ToolBar.CreateEx(this, dwCtrlStyle,
dwStyle, CRect(1, 1, 1, 1), IDR_TOOLBAR))
{
dwStyle = CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC;
m_ToolBar.SetPaneStyle(m_ToolBar.GetPaneStyle() | dwStyle);
CMFCToolBarInfo info;
info.m_uiColdResID = IDB_PNG_MAIN_TOOLBAR;
info.m_uiHotResID = IDB_PNG_MAIN_TOOLBAR;
info.m_uiLargeColdResID = IDB_PNG_MAIN_TOOLBAR;
info.m_uiLargeHotResID = IDB_PNG_MAIN_TOOLBAR;
m_ToolBar.LoadToolBarEx(IDR_TOOLBAR, info, FALSE);
CSize sizeToolBar = m_ToolBar.CalcFixedLayout(TRUE, TRUE);
m_ToolBar.SetWindowPos(NULL, 0, 0, sizeToolBar.cx, sizeToolBar.cy,
SWP_NOACTIVATE | SWP_NOZORDER);
// Move all controls down
CPoint ptOffset(0, sizeToolBar.cy);
CRect rcChild;
CWnd* pwndChild = GetWindow(GW_CHILD);
while (pwndChild)
{
if (pwndChild->GetSafeHwnd() != m_ToolBar.GetSafeHwnd())
{
pwndChild->GetWindowRect(rcChild);
ScreenToClient(rcChild);
rcChild.OffsetRect(ptOffset);
pwndChild->MoveWindow(rcChild, FALSE);
}
pwndChild = pwndChild->GetNextWindow();
}
// Resize the window
CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.bottom += sizeToolBar.cy;
MoveWindow(rcWindow, FALSE);
}
}
In short I had to manually move down all controls and resize the window. Looks OK now:
It is a bit difficult to provide you with a minimal working example here but I am going to try and explain this issue that I have only just noticed.
The Context
So, I have a regular CDialogEx derived class, defined like this:
class CChristianLifeMinistryStudentsDlg : public CDialogEx
I have set it up so that the borders will not resize:
The main application (also CDialogEx based) has a fixed window. That behaves correct.
From the menu the user displays a resizable dialogue (an editor).
On this dialog is a button the user can press which will in turn display the popup modal dialog I am referring to.
What Happens
When this dialog is displayed I have noticed this when you hover the mouse over the dialog borders:
I don't understand why this is happening.
Cursor Management
In the "editor" that spawns this popup window I do have some cursor management like this:
BOOL CChristianLifeMinistryEditorDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (CPersistentWaitCursor::WaitCursorShown())
{
RestoreWaitCursor();
return TRUE;
}
return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}
But, I have tried temporarily to invoke this popup from my main application dialog which does not have an cursor management and the result is still the same.
Spy Results
As requested I have just used Spy to examine the window styles:
As anticipated we suddenly have WS_THICKFRAME set, when it was not in the resource editor!
So
In my RC file the dialog has the DS_MODALFRAME flag set but at runtime it ends up having the WS_THICKFRAME set. As far as I am aware I never make these changes for these affected dialog objects.
Update
I have found out the following:
BOOL CChristianLifeMinistryStudentsDlg::OnInitDialog()
{
LONG_PTR lStyle = GetWindowLongPtr(GetSafeHwnd(), GWL_STYLE);
if (lStyle & WS_THICKFRAME)
AfxMessageBox(_T("Thick"));
else if (lStyle & DS_MODALFRAME)
AfxMessageBox(_T("Modal"));
CDialogEx::OnInitDialog();
If I put the check code before the CDialogEx::OnInitDialog(); call the style is set as DS_MODALFRAME. But if I put the same check code after the CDialogEx::OnInitDialog(); call it is then changed to WS_THICKFRAME. Why?
OK
So, the CDialogEx::OnInitDialog method calls CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName). This in turn calls CWnd::InitDynamicLayout(). And in that method it does this:
if (!bIsChild && (pDialog != NULL || pPropSheet != NULL))
{
CRect rect;
GetClientRect(&rect);
ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
::AdjustWindowRectEx(&rect, GetStyle(), ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
}
There we go. So it is because I am using CDialogEx as my base class. Is this a bug in MFC?
Clarification
The "Editor" (parent window of the popup that owns the button) does use dynamic layout functonality:
But in this instance the popup does not need to. But it is because my popup is derived from CDialogEx that this is happening.
The plot thickens
So this is the MFC code that is always called with CDialog::OnInitDialog:
BOOL CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName)
{
if (GetSafeHwnd() == NULL || !::IsWindow(GetSafeHwnd()) || lpszResourceName == NULL)
{
return FALSE;
}
// find resource handle
DWORD dwSize = 0;
LPVOID lpResource = NULL;
HGLOBAL hResource = NULL;
if (lpszResourceName != NULL)
{
HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_DIALOG_LAYOUT);
HRSRC hDlgLayout = ::FindResource(hInst, lpszResourceName, RT_DIALOG_LAYOUT);
if (hDlgLayout != NULL)
{
// load it
dwSize = SizeofResource(hInst, hDlgLayout);
hResource = LoadResource(hInst, hDlgLayout);
if (hResource == NULL)
return FALSE;
// lock it
lpResource = LockResource(hResource);
ASSERT(lpResource != NULL);
}
}
// Use lpResource
BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize);
// cleanup
if (lpResource != NULL && hResource != NULL)
{
UnlockResource(hResource);
FreeResource(hResource);
}
if (bResult)
{
InitDynamicLayout();
}
return bResult;
}
For some reason this call BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize); is return TRUE. As a result the dialog eventually calls InitDynamicLayout. In my other dialogs that are popups this does not happen. Instead, bResult ends up as FALSE and thus the frame is not resized.
So why does it think it worked?
Worked it out. I don't remember doing this but for some reason some of my controls on the dialog had dynamic properties set. For example:
I had to set all of these properties back to None. Then it behaved.
You can easily tell if a given dialog resource has any dynamic properties by opening your resource file in a text editor. For example:
IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
BEGIN
0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
END
If something like the above is present then your dialog will be deemed as having a dynamic layout, and thus the settings for the dialog are modified:
ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
The resource will look like this when it has no dynamic control properties:
IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
BEGIN
0
END
I chose to manually reset each control via the IDE. However, I guess you could modify the text file manually.
As to why I had controls with dynamic properties in the first place, well, I can't tell you. I might have been fiddling in the past with the dialog and not realised the side effect to the border frame. Or, possibly, I may have copied controls from one resource on to another and it carried the dyanmic values.
The interesing side note is that whilst the MFC code set the border as thick, it did not change it sufficiently to enable dialog resizing. But that is another matter!
At least we now know the cause of the issue and how to easily identify the dialogs in the resource that have dynamic layouts.
In a C++ app I create a button using CreateWindowEx and later try to change its position using SetWindowPos, but the button doesn't appear where I want it.
What's interesting is that when I resize the window (with the mouse, not programatically), I can see for a split second a blank silhouette the same size of the button where the button is supposed to appear. This must be because I also call SetWindowPos in response to window resizing events. However the actual button stays at the same location. I'd post a screenshot but for some reason the silhouette never shows up in screenshots.
This is the code that changes the X position (the code that changes the Y position is almost identical):
HRESULT Control::put_Left(float left)
{
RECT windowRect;
::GetWindowRect(m_hWnd, &windowRect);
if (m_isTopLevel)
{
BOOL bResult = ::SetWindowPos(
m_hWnd,
nullptr,
static_cast<int>(DesktopDpi::GetInstance().DipsToPixelsX(left)),
windowRect.top,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOREPOSITION
);
if (!bResult)
return HRESULT_FROM_WIN32(::GetLastError());
}
else
{
// NOTE: for a button this is the code that will execute, because a
// button is not a top-level window
HWND hWndParent = ::GetParent(m_hWnd);
if (hWndParent == nullptr)
return HRESULT_FROM_WIN32(::GetLastError());
POINT parentPos = {0, 0};
if (!::ClientToScreen(hWndParent, &parentPos))
return E_FAIL;
BOOL bResult = ::SetWindowPos(
m_hWnd,
nullptr,
static_cast<int>(DesktopDpi::GetInstance().DipsToPixelsX(left)),
windowRect.top - parentPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOREPOSITION
);
if (!bResult)
return HRESULT_FROM_WIN32(::GetLastError());
}
return S_OK;
}
Are you sure the parent of the button isn't moving it back to the old position when it handles WM_SIZE? It sure sounds like it from your description.