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.
Goal
I am creating a simple chat client GUI with WinApi.
I'm trying to make the layout responsive so the controls change size / position when the main window is resized and here comes the issue.
Problem
Sometimes when the main window is resized, the controls on the right bottom section are not displayed correctly. The new position is fine but they are not painted whole. If I left-click on them, they are displayed correctly again.
This doesn't happen on every resize event, but usually after few attempts.
You can see in the screenshots below:
Default size: https://ibb.co/BNMTM37
Resized: https://ibb.co/6NJqgXp
Resized 2nd ex.: https://ibb.co/dkjVHmt
Code
Creating the controls
//Rigt bottom box - User nickname area
nicknameArea = CreateWindowEx(WS_EX_CLIENTEDGE, L"static", L"", WS_VISIBLE | WS_CHILD | WS_BORDER,
WinDimensions::textOutputWidth + 2 * WinDimensions::margin, WinDimensions::textOutputHeight + 2 * WinDimensions::margin, 150, WinDimensions::editCtrlHeight,
m_hwnd, (HMENU)IDC_NICKNAME_AREA, 0, 0);
currentNickname = CreateWindowA("static", "Your nickname:", WS_VISIBLE | WS_CHILD, // "Your nickname" label
WinDimensions::margin, WinDimensions::margin * 1, 120, 20,
m_hwnd, (HMENU)0, 0, 0);
nicknameEditControl = CreateWindowA("edit", "", WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, // Edit ctrl to change nick
WinDimensions::margin, WinDimensions::margin * 3, 100, 20,
m_hwnd, (HMENU)0, 0, 0);
changeNicknameButton = CreateWindowA("button", "Change name", WS_VISIBLE | WS_CHILD, // Button "Change nickname"
WinDimensions::textOutputWidth + 2 * WinDimensions::margin, WinDimensions::textOutputHeight + 6 * WinDimensions::margin, 100, 25,
m_hwnd, (HMENU)BUTTON_CHANGENICKNAME, 0, 0);
Note: I've already tried creating the controls with CreateWindowEx (a suggestion I found in a different topic). Also nicknameArea was previously created with CreateWindowA but the behavior remains the same.
Function called on WM_SIZE message:
RECT winRect;
if (GetWindowRect(m_hwnd, &winRect)) {
//Calculating new positions / dimensions
int newWidth = winRect.right - winRect.left;
int newHeight = winRect.bottom - winRect.top;
int rightBoxNewX = newWidth - WinDimensions::rightBarSize + (2 * WinDimensions::margin);
int bottomBoxesNewY = newHeight - WinDimensions::editCtrlHeight - WinDimensions::buttonYSpace + (2 * WinDimensions::margin);
//Resizing the text output window
SetWindowPos(textOutput, NULL, NULL, NULL,
newWidth - WinDimensions::rightBarSize, newHeight - WinDimensions::editCtrlHeight - WinDimensions::buttonYSpace,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
//Resizing message input's width and repositioning in Y axes
SetWindowPos(editControl, NULL, WinDimensions::margin, bottomBoxesNewY,
newWidth - WinDimensions::rightBarSize, WinDimensions::editCtrlHeight,
SWP_NOZORDER | SWP_NOACTIVATE);
//Repositioning the send button
SetWindowPos(sendButton, NULL, WinDimensions::margin, newHeight - WinDimensions::buttonYSpace + (3 * WinDimensions::margin), 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
//Repositioning the right side column boxes
SetWindowPos(onlineUsers, NULL,
rightBoxNewX, WinDimensions::margin, WinDimensions::rightBarSize - (5 * WinDimensions::margin), bottomBoxesNewY - (2 * WinDimensions::margin), SWP_NOZORDER | SWP_NOACTIVATE);
//THESE controls are not working correctly after resizing
SetWindowPos(nicknameArea, NULL,
rightBoxNewX, bottomBoxesNewY, NULL, NULL, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
MoveWindow(changeNicknameButton,
rightBoxNewX + WinDimensions::margin, bottomBoxesNewY + 7 * WinDimensions::margin,
100, 25, 0);
MoveWindow(nicknameEditControl,
rightBoxNewX + WinDimensions::margin, bottomBoxesNewY + 4 * WinDimensions::margin,
100, 20, 0);
MoveWindow(currentNickname,
rightBoxNewX + WinDimensions::margin, bottomBoxesNewY + WinDimensions::margin,
120, 20, 0);
}
Note: I used MoveWindow instead of SetWindowPos for the three controls above, because SetWindowPos created much worse-looking results regardless of the flags I put in there
WM_PAINT:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(m_hwnd, &ps);
}
Note: This is just to fill the background of the main window with white.
I've also tried to make those three faulty controls set to the same HWND parrent, in this case the static control (HWND nicknameArea). The resizing then works fine, but I lose the functionality of my button. And since my knowledge of WinApi isn't very extensive, I can't get my head around subclassing and it always fails somewhere for me (I've seen many tutorials but I always fail to implement it in my existing program).
I'd appreciate any comments or suggestions.
Thank you.
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 can't seem to add the certain dialog box in the TAB CONTROL that i've created.
Can someone help me?
I've created tab items (TCITEM) and the tab control using CreateWindow.
tab_handle is an HWND global variable.
//create items for tab
TCITEM tab1Item;
tab1Item.mask = TCIF_TEXT;
tab1Item.pszText = "Tab 1";
TCITEM tab2Item;
tab2Item.mask = TCIF_TEXT;
tab2Item.pszText = "Tab 2";
//create tab
CreateWindow(WC_TABCONTROL, "Test", TCS_FLATBUTTONS | WS_CHILD | WS_VISIBLE, 10, 20, 450, 230, this->m_hWnd, (HMENU) IDD_DLGTAB1, (HINSTANCE)GetWindowLong(this->m_hWnd, GWL_HINSTANCE), NULL);
//getting of tab
tab_handle = GetDlgItem(this->m_hWnd, IDD_DLGTAB1);
//inserting of tab items in tab
RECT tab_rectangle;
GetClientRect(tab_handle, &tab_rectangle);
int width = (tab_rectangle.right - tab_rectangle.left);
int height = (tab_rectangle.bottom - tab_rectangle.top);
//create dialog
HWND dialog_handle = CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG2), tab_handle, (DLGPROC) Tab1Dlg::DlgProc, (LPARAM) lParam);
TabCtrl_InsertItem(tab_handle, 0, &tab1Item);
TabCtrl_InsertItem(tab_handle, 1, &tab2Item);
MoveWindow(dialog_handle, tab_rectangle.left+20, tab_rectangle.top+20,(width - 300),(height - 300), TRUE);
ShowWindow(dialog_handle, SW_SHOW);
You have to create the dialog first. Then create the tab control as a child control of the dialog.
It's easier to use the dialog editor to drag and drop a tab control in to dialog. Then you can skip CreateWindow(WC_TABCONTROL...) and use GetDlgItem(dialog_handle, IDC_TAB1) to find the tab control.
You also need to create 2 border-less child dialogs in resource editor (not popup dialog). Then use CreateDialog(0, MAKEINTRESOURCE(IDD_PAGE1), dialog_handle, TabChildProc) to put child dialogs inside the tab.
If making a modal dialog, you may want to use DialogBox instead of CreateDialogParam and do the initialization in WM_INITDIALOG
HINSTANCE hinst = GetModuleHandle(NULL);
HWND dialog_handle = CreateDialogParam(hinst,
MAKEINTRESOURCE(IDD_DIALOG2), 0, (DLGPROC)Tab1Dlg::DlgProc, (LPARAM)0);
ShowWindow(dialog_handle, SW_SHOW);
RECT rc;
GetClientRect(dialog_handle, &rc);
CreateWindow(WC_TABCONTROL, "Test", TCS_FLATBUTTONS | WS_CHILD | WS_VISIBLE,
rc.left + 10, rc.top + 10,
rc.right - 20, rc.bottom - 20 - 30,
dialog_handle, (HMENU)IDC_TAB1, hinst, NULL);
tab_handle = GetDlgItem(dialog_handle, IDC_TAB1);
TCITEM tci = { 0 };
tci.mask = TCIF_TEXT;
char buf[50];
tci.pszText = buf;
strcpy_s(buf, "Page1");
tci.cchTextMax = strlen(buf);
TabCtrl_InsertItem(tab_handle, 0, &tci);
strcpy_s(buf, "Page2");
tci.cchTextMax = strlen(buf);
TabCtrl_InsertItem(tab_handle, 1, &tci);
Hello I want to know if it is possible to change the font of an edit control for some lines only without affecting the remaining:
In my Edit control I have a text but I want some headlines and titles in bigger font and bold while the other lines are with smaller font.
I tried SendMessage(hEdit, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(0, true));
But it sets the whole text in the passed in font.
I thought some messing up with SelectObject(hDcEdit, hFont); But I don't know if it is correct and how.
A standard Edit Control (think, Notepad) does not support what you are looking for. It only supports one Font for the entire text.
What you are looking for is a RichEdit Control instead (think, Wordpad), and in particular its EM_SETCHARFORMAT message, which can be used to apply different formatting (including fonts, colors, etc) to different sections of text.
This is not working with the default Editcontrol, but you can use a Richeditcontrol
#include <Windows.h>
#include <CommCtrl.h>
HINSTANCE relib = LoadLibrary("riched32.dll");
if (relib == NULL) {
MessageBox(NULL, "couldn't load richedit32.dll", "", MB_ICONEXCLAMATION);
hEdit = CreateWindow(RICHEDIT_CLASS, "", WS_VISIBLE | WS_CHILD | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL | WS_VSCROLL | WS_HSCROLL, 0, 0, 200, 200, hWnd, NULL,
NULL, NULL);
Now to set the font to your Richeditcontrol use:
CHARFORMAT2 cf;
memset(&cf, 0, sizeof cf);
cf.cbSize = sizeof cf;
cf.dwMask = CFM_FACE;
wsprintf(cf.szFaceName, "Arial"); //Here you can set the fontname you wont (C:/Windows/Fonts)
SendMessage(hEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);