How do I create a normal win32 edit control? - c

I'm trying to create an edit control with the regular 3D border around it (in the classic windows style, anyway), but it just has a 1px black border around it. Here is my CreateWindowEx call:
return CreateWindowEx(0, "EDIT", "E:\\bk",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_LEFT,
87, 81, 150, 17,
main_window.hwnd,
(HMENU)5, hInstance, NULL);
If I exclude WS_BORDER then it's just a white box. Any ideas on what's wrong here?
Update
WS_EX_CLIENTEDGE did the trick.
I don't know anything about manifest files, or how to make the window use the more modern windows themes (XP, for example), instead of the chunky 3D borders. But, when I do learn all that, will WS_EX_CLIENTEDGE make them use those themes instead, or will it enforce the 3D look?

Try using WS_EX_CLIENTEDGE. That will create an inset 3-D window border under typical situations.
return CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "E:\\bk",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_LEFT,
87, 81, 150, 17,
main_window.hwnd,
(HMENU)5, hInstance, NULL);
Also see the following link for the rest of the available flags for CreateWindowEx.
CreateWindowEx at MSDN

He is right WS_EX_CLIENTEDGE will do the 3D border.

I think you mean the 'WS_EX_DLGMODALFRAME' style. It makes it look like the old-style 3d raised type look. Combined with 'WS_BORDER' to make it look a like a 3d border around the control.

Below code Creates an input box and a button, when clicked updates title.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int main() {
HINSTANCE hInstance;
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Edit control";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Edit control",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, 220, 220, 280, 200, 0, 0,
hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static HWND hwndEdit;
HWND hwndButton;
switch (msg) {
case WM_CREATE:
hwndEdit = CreateWindowW(L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 50, 150, 20, hwnd, (HMENU)102, NULL, NULL);
hwndButton = CreateWindowW(L"button", L"Set title", WS_VISIBLE | WS_CHILD,
50, 100, 80, 25, hwnd, (HMENU)103, NULL, NULL);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
int len = GetWindowTextLengthW(hwndEdit) + 1;
wchar_t text[len];
GetWindowTextW(hwndEdit, text, len);
SetWindowTextW(hwnd, text);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
To compile:
g++ test.cpp -o test.exe -mwindows
source: zetcode.com/editcontrol

Related

Win32 API Disable User Input in Drop Down / Combobox

This sample code I found online has a list dropdown menu, and when I click the text box that the items are displayed in, it doesn't allow me to type anything into the box. This is the behavior I want for my own code. However when I paste in the relevant parts of this code into my own, my program is allowing me to type characters into the Drop Down box. I can't find a single flag in this code that is causing the user input to be disabled, but it only works properly here and not in my code for some reason.
I even tried commenting out certain lines of the code to see if they were responsible for disabling user input, but it is still disabled. Yet in my code, no matter what I do, I can type additional characters into the list box as if it were any other text field. I can't figure out what I'm missing.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = "Application";
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
g_hinst = hInstance;
RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, "Combo box",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 270, 170, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
static HWND hwndCombo;
//static HWND hwndStatic;
const char *items[] = { "FreeBSD", "OpenBSD",
"NetBSD", "Solaris", "Arch" };
switch(msg) {
case WM_CREATE:
hwndCombo = CreateWindow("Combobox", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
10, 10, 120, 200, hwnd, NULL, g_hinst, NULL);
// CreateWindowW(L"Button", L"Drop down",
// WS_CHILD | WS_VISIBLE,
// 150, 10, 90, 25, hwnd, (HMENU) 1, g_hinst, NULL);
// hwndStatic = CreateWindowW(L"Static", L"",
// WS_CHILD | WS_VISIBLE,
// 150, 80, 90, 25, hwnd, NULL, g_hinst, NULL);
for (int i = 0; i < 4; i++ ) {
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}
break;
case WM_COMMAND:
// if (HIWORD(wParam) == BN_CLICKED) {
// SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
// }
if (HIWORD(wParam) == CBN_SELCHANGE) {
LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
// SetWindowTextW(hwndStatic, items[sel]);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
There is no special handling, no character limit flags, no additional source code or headers, or resource files, nothing...
I've read this code over and over and I just don't understand what is causing this program to present the dropdown items as read-only text.
My code is pretty much identical with just some additional switch statements and functions, but the functionality of the combo box should be the same. Is there some other global flag in a program that could cause user input where it shouldn't be allowed?
Use the CBS_DROPDOWNLIST style on the ComboBox to remove the writable edit control
hwndCombo = CreateWindow("Combobox", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
10, 10, 120, 200, hwnd, NULL, g_hinst, NULL);
Per the documentation:
Combo Box Styles
To create a combo box using the CreateWindow or CreateWindowEx function, specify the COMBOBOX class, appropriate window style constants, and a combination of the following combo box styles.
Constant
Description
...
...
CBS_DROPDOWN
Similar to CBS_SIMPLE, except that the list box is not displayed unless the user selects an icon next to the edit control.
CBS_DROPDOWNLIST
Similar to CBS_DROPDOWN, except that the edit control is replaced by a static text item that displays the current selection in the list box.
...
...
CBS_SIMPLE
Displays the list box at all times. The current selection in the list box is displayed in the edit control.
...
...

In WIndows API, under what "event" do I put child control creation or why aren't my controls appearing?

It's been years since I last worked with Windows API and I'm trying my hand at it again. I have a simple Window (with title "Test"). I put in the Window message handler under WM_CREATE, 2 CreateWindow calls to create a static and an edit control. I'm pretty sure the coordinates are decent (not overlapping or off the window rectangle). Am I putting the calls under the wrong event? It's just a simple window - not MDI or SDI or anything like that. Here is my code for the entire program. I hope it's sufficient to figure out what I'm doing wrong. I'm using Eclipse CDT with Cygwin's G++ compiler to build it....:
#include <windows.h>
BOOL InitApplication(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "";
wc.lpszClassName = "MainWindow";
return(RegisterClass(&wc));
}
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
HINSTANCE hCurInstance = hinstance;
HWND hWnd = CreateWindow("MainWindow", "Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)(NULL), (HMENU)(NULL), hCurInstance, (LPVOID)(NULL));
if(!hWnd)
return(FALSE);
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return(TRUE);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
CreateWindow("WC_STATIC", "&Test: ", SS_LEFT | WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
CreateWindow("WC_EDIT", "", WS_BORDER | WS_TABSTOP | WS_VISIBLE | WS_CHILD | ES_LEFT, 60, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
return(0);
default:
return(DefWindowProcA(hWnd, uMsg, wParam, lParam));
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmd, int nCmdShow)
{
BOOL fGotMessage;
MSG msg;
if(!InitApplication(hInstance))
return(FALSE);
if(!InitInstance(hInstance, nCmdShow))
return(FALSE);
while(((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) && (fGotMessage != -1))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
WM_CREATE is the correct "event" message to create the child controls in.
You are simply using the wrong class names for the child controls. That is why you are not seeing them. Had you checked the result of those CreateWindow() calls for failures, you would have noticed that CreateWindow() was returning NULL, and GetLastError() was reporting ERROR_CANNOT_FIND_WND_CLASS (1407).
You need to replace "WC_STATIC" with "Static", and replace "WC_EDIT" with "Edit". Or, you can use the pre-defined WC_STATIC and WC_EDIT constants that are defined in <commctrl.h>.
You can also replace GetWindowLongPtr(hWnd, GWLP_HINSTANCE) with NULL when creating system-defined classes, as they are registered globally, not per-module. The HINSTANCE parameter of CreateWindow/Ex() is ignored for them.
On a side note: your GetMessage() loop can be simplified to just:
while (GetMessage(&msg, (HWND) NULL, 0, 0))
See: When will GetMessage return -1?

error LNK1120: 1 unresolved external - VS13 C [duplicate]

This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 6 years ago.
I have tried to compile this code:
#include <windows.h>
#include <commctrl.h>
#define ID_TABCTRL 1
#define ID_EDIT 2
#define BTN_ADD 3
#define BTN_DEL 4
#define BTN_CLR 5
#define MAX_TAB_LEN 15
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hTab, hEdit;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = { 0 };
wc.lpszClassName = L"Tab control";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Tab control",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 380, 230, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
WINCOMMCTRLAPI BOOL WINAPI InitCommonControlsEx(_In_ const INITCOMMONCONTROLSEX *picce);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
TCITEMW tie;
wchar_t text[4];
LRESULT count, id;
INITCOMMONCONTROLSEX icex;
switch (msg) {
case WM_CREATE:
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
hTab = CreateWindowW(WC_TABCONTROLW, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 200, 150, hwnd, (HMENU)ID_TABCTRL, NULL, NULL);
hEdit = CreateWindowW(WC_EDITW, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
250, 20, 100, 25, hwnd, (HMENU)ID_EDIT, NULL, NULL);
SendMessage(hEdit, EM_SETLIMITTEXT, MAX_TAB_LEN, 0);
CreateWindowW(WC_BUTTONW, L"Add", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 50, 100, 25, hwnd, (HMENU)BTN_ADD, NULL, NULL);
CreateWindowW(WC_BUTTONW, L"Delete", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 80, 100, 25, hwnd, (HMENU)BTN_DEL, NULL, NULL);
CreateWindowW(WC_BUTTONW, L"Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 110, 100, 25, hwnd, (HMENU)BTN_CLR, NULL, NULL);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case BTN_ADD:
GetWindowTextW(hEdit, text, 250);
if (lstrlenW(text) != 0) {
tie.mask = TCIF_TEXT;
tie.pszText = text;
count = SendMessageW(hTab, TCM_GETITEMCOUNT, 0, 0);
SendMessageW(hTab, TCM_INSERTITEMW, count,
(LPARAM)(LPTCITEM)&tie);
}
break;
case BTN_DEL:
id = SendMessageW(hTab, TCM_GETCURSEL, 0, 0);
if (id != -1) {
SendMessageW(hTab, TCM_DELETEITEM, 0, id);
}
break;
case BTN_CLR:
SendMessageW(hTab, TCM_DELETEALLITEMS, 0, 0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return(DefWindowProcW(hwnd, msg, wParam, lParam));
}
and it seems like this libary:
#include <commctrl.h>
giving me the following error:
error LNK2019: unresolved external symbol __imp__InitCommonControlsEx#4 referenced in function _WndProc#16 C:\Users\User\Desktop\Magshimim\Magshimim EX1\Magshimim EX1\01.obj Magshimim EX1
now the code is an example code from here: http://zetcode.com/gui/winapi/advancedcontrols/
so i figured out that something wrong with my compiler...
can any one help me find the problem and correct it?
edit:
the question is not a duplicate because the refereed duplicate talks in a very general way about the error itself and as a beginner i dont have the skills to build a proper answer from such a question and because that code is been used by many peoples who check the example i think its impotent to make a specific question
Either add #pragma comment(lib, "comctl32.lib") or adjust the linker settings to link against comctl32.lib.
You can check the table at the bottom of a function's MSDN article to find out, which library you are required to link against. Every Windows application is linked against kernel32.dll and every GUI application against user32.dll. Anything else needs to be specified explicitly1.
1 There are exceptions, check IInspectable's comment below.

Why can't I seem to add buttons to a toolbar? [WINAPI]

For some reason, as soon as I try to separate certain blocks of code into different functions, adding buttons (bitmaps included) to a toolbar isn't working anymore. Having them put together in one place however works like a charm, however.
Yet I can't figure out the reason for this. Maybe a pointer isn't working as expected...
The expected output is this:
The relevant code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateUserInterface(hwnd);
break;
...
}
return 0;
}
void CreateUserInterface(HWND hwnd)
{
HFONT hfDefault;
HWND hEdit;
HWND hTool;
TBADDBITMAP tbab;
TBBUTTON tbb[TBBSIZE];
HWND hStatus;
int statWidths[] = {100, -1};
// create edit control
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
"EDIT",
"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
0, 0, 100, 100,
hwnd, (HMENU) IDC_MAIN_EDIT,
GetModuleHandle(NULL), NULL);
if(hEdit == NULL)
{
MessageBox(hwnd, "Could not create edit box!", "Error!",
MB_OK | MB_ICONERROR);
}
hfDefault = GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM) hfDefault, MAKELPARAM(FALSE, 0));
// create toolbar
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
if(hTool == NULL)
{
MessageBox(hwnd, "Could not create tool bar!", "Error!",
MB_OK | MB_ICONERROR);
}
SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
tbab.hInst = HINST_COMMCTRL;
tbab.nID = IDB_STD_SMALL_COLOR;
SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM) &tbab);
ZeroMemory(tbb, sizeof tbb);
tbb[0].iBitmap = STD_FILENEW;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = TBSTYLE_BUTTON;
tbb[0].idCommand = ID_FILE_NEW;
tbb[1].iBitmap = STD_FILEOPEN;
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = TBSTYLE_BUTTON;
tbb[1].idCommand = ID_FILE_OPEN;
tbb[2].iBitmap = STD_FILESAVE;
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = TBSTYLE_BUTTON;
tbb[2].idCommand = ID_FILE_SAVE_AS;
SendMessage(hTool, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM) &tbb);
}
What I get is:
The relevant code:
#define TBBSIZE 1
void CreateUserInterface(HWND hwnd)
{
...
HWND hTool;
TBADDBITMAP tbab;
TBBUTTON tbb[TBBSIZE];
...
CreateToolbar(hwnd, hTool);
InitializeBitmap(hTool, &tbab);
InitializeButtons(htool, tbb, TBBSIZE);
...
}
void CreateToolbar(HWND hwnd, HWND hTool)
{
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
if(hTool == NULL)
{
MessageBox(hwnd, "Could not create tool bar!", "Error!",
MB_OK | MB_ICONERROR);
}
SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
}
void InitializeBitmap(HWND hTool, TBADDBITMAP *tbab)
{
(*tbab).hInst = HINST_COMMCTRL;
(*tbab).nID = IDB_STD_SMALL_COLOR;
SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM) tbab);
}
void InitializeButtons(HWND hTool, TBBUTTON *tbb, int size)
{
ZeroMemory(tbb, sizeof(*tbb) * size);
tbb[size-size].iBitmap = STD_FILENEW;
tbb[size-size].fsState = TBSTATE_ENABLED;
tbb[size-size].fsStyle = TBSTYLE_BUTTON;
tbb[size-size].idCommand = ID_FILE_NEW;
SendMessage(hTool, TB_ADDBUTTONS, size, (LPARAM) tbb);
...
}
(Don't mind the status bar to the bottom right, I forgot to include it in the code in the first example)
For obvious reasons the problem must lie somewhere in the bit that handles adding the bitmaps and buttons. But what it is I don't know...what am I missing?
Edit1: To make it read easier I have removed the two additional buttons (less code). It works the same way still, i.e. not working at all. ;-)
EDIT2: Thanks to HostileFork I found out that a windows handle doesn't work quite the same way as a normal raw pointer in C. The solution was to pass the address of hTool to the CreateToolbar function:
#define TBBSIZE 1
void CreateUserInterface(HWND hwnd)
{
...
HWND hTool;
...
CreateToolbar(hwnd, &hTool);
...
}
void CreateToolbar(HWND hwnd, HWND *hTool)
{
*hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
... // and so on
}
It looks like you're assigning to an argument, which will only affect it for the duration of the function:
void CreateToolbar(HWND hwnd, HWND hTool)
{
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
...
}
You need to either make hTool a return value, or pass it by reference or pointer so that its value can be bubbled up to the caller and used in the other functions...
Does InitializeButtons have a default value for the third parameter, size? If so, check what it is, as you don't appear to be sending this value in the call to the method. My guess is that the parameter defaults to zero, so this will be passed in the SendMessage call.
If InitializeButtons doesn't have a default value for the third parameter, then either you have a typo in your example source, you have two InitializeButtons methods with different numbers of parameters (and hence are calling the wrong one), or your code shouldn't compile :)

TabControl clips client area when displaying overflow arrows

This bug appears only with common control v6 (theme enabled) on XP (seems to work on 7 and 2008). I wonder if someone else might have seen this bug feature.
When you have a single-line TabControl with lots of tabs, a pairs of arrows should appear if there is not enough space to display all the tabs. This is all nice except that the client area is also clipped, which is not nice at all.
Have I miss something ? I played with tabcontrol's window style, but no luck so far.
To illustrate this, it's actually best to see it in action:
#define UNICODE
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
HWND htab, hbut;
int WINAPI WinMain (HINSTANCE instance,
HINSTANCE previnst,
LPSTR args,
int wndState)
{
int i;
MSG messages;
WNDCLASSEX wincl = {
.hInstance = instance, .lpszClassName = L"WindowsApp",
.lpfnWndProc = WindowProcedure, .style = CS_DBLCLKS, .cbSize = sizeof wincl,
.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1)
};
InitCommonControls();
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClassEx (&wincl))
return 0;
HWND hwnd = CreateWindow(L"WindowsApp", L"Windows App", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, instance, NULL);
htab = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL,
L"MyTab", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 10, 10, 514, 325, hwnd,
(HMENU) 10, instance, NULL);
hbut = CreateWindow(
WC_BUTTON, L"My nice button that is clipped", WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 30, 494, 285, htab, (HMENU) IDOK, instance, NULL
);
for (i = 0; i < 10; i ++)
{
WCHAR myBuf[100];
TCITEM tc = {.mask = TCIF_TEXT, .pszText = myBuf};
wsprintf(myBuf, L"My super tab %d", i + 1);
TabCtrl_InsertItem(htab, i, &tc);
}
SendMessage(hbut, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
SendMessage(htab, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
ShowWindow(hwnd, wndState);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT r;
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
GetClientRect(hwnd, &r);
MoveWindow(htab, 10, 10, r.right-20, r.bottom - 20, TRUE);
MoveWindow(hbut, 10, 30, r.right-40, r.bottom - 60, TRUE);
break;
case WM_COMMAND:
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Unfortunately I can't test it (I have Win7) but according to http://msdn.microsoft.com/en-us/library/hh298367%28v=VS.85%29.aspx you need to have clip siblings on the tab control and also the parent window. You could also try using the tab control's adjust rect message to get the positions for your button.

Resources