In order to add a toolbar to my MFC dialog baced class, I tried all ways of adding resources>toolbars but they didn't work. Finally I came up to the point to create a toolbar dynamically. This is the code that I have used:
Resource.h
#define IDB_PanTbrBtn 139
#define IDB_NewTbrBtn 140
#define IDB_ZoomInTbrBtn 141
#define IDB_ZoomOutTbrBtn 142
#define IDC_FirstToolBar 1011
#define IDC_NEWTBRBTN 1012
#define IDC_ZOOMINTBRBTN 1013
#define IDC_ZOOMOUTTBRBTN 1014
#define IDC_PANTBRBTN 1015
InitialJobProject2Dlg.h : header file for the dialog baced project
#pragma once
#include "WndResizer.h"
#include "afxdlgs.h"
#include "FilesWorkFlow.h"
#include "OpenGLControl.h"
CWndResizer m_resizer;
CMFCToolBar m_FirstToolBar;
FilesWorkFlow *m_files;
COpenGLControl *m_oglWindow;
InitialJobProject2Dlg.cpp : the codes related to the toolbar in the function OnInitDialog()
bool bAnchored = false;
bAnchored = m_resizer.Hook(this);
assert(bAnchored);
bool ToolbarCreated = m_FirstToolBar.CreateEx(this, AFX_DEFAULT_TOOLBAR_STYLE, 100 );
if(ToolbarCreated)
{
m_FirstToolBar.SetDlgCtrlID(IDC_FirstToolBar);
bAnchored = m_resizer.SetAnchor(IDC_FirstToolBar,ANCHOR_LEFT | ANCHOR_TOP);
assert(bAnchored);
m_FirstToolBar.SetPaneStyle(m_FirstToolBar.GetPaneStyle() & ~(CBRS_GRIPPER|CBRS_SIZE_DYNAMIC|CBRS_BORDER_ANY));
VERIFY(m_FirstToolBar.LoadBitmapW(IDB_NewTbrBtn));
VERIFY(m_FirstToolBar.GetImages()->Load(IDB_NewTbrBtn,nullptr,TRUE));
int imageIndex = m_FirstToolBar.GetImages()->GetCount();
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_NEWTBRBTN,imageIndex));
VERIFY(m_FirstToolBar.LoadBitmapW(IDB_PanTbrBtn));
VERIFY(m_FirstToolBar.GetImages()->Load(IDB_PanTbrBtn,nullptr,TRUE));
imageIndex = m_FirstToolBar.GetImages()->GetCount();
m_FirstToolBar.InsertButton( CMFCToolBarButton( IDC_PANTBRBTN,imageIndex));
VERIFY(m_FirstToolBar.LoadBitmapW(IDB_ZoomInTbrBtn));
VERIFY(m_FirstToolBar.GetImages()->Load(IDB_ZoomInTbrBtn,nullptr,TRUE));
imageIndex = m_FirstToolBar.GetImages()->GetCount();
m_FirstToolBar.InsertButton( CMFCToolBarButton( IDC_ZOOMINTBRBTN,imageIndex));
VERIFY(m_FirstToolBar.LoadBitmapW(IDB_ZoomOutTbrBtn));
VERIFY(m_FirstToolBar.GetImages()->Load(IDB_ZoomOutTbrBtn,nullptr,TRUE));
imageIndex = m_FirstToolBar.GetImages()->GetCount();
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_ZOOMOUTTBRBTN,imageIndex));
CSize size = m_FirstToolBar.CalcFixedLayout( FALSE, TRUE );
m_FirstToolBar.SetWindowPos( NULL, 0, 0, size.cx + 10, size.cy + 10 , SWP_NOACTIVATE | SWP_NOZORDER );
}
this is my project's resource view:
and this one is the res folder of my program:
images that I want to be shown as the icons of toolbar buttons are 48x48 ,32 bit depth bitmap images but I had the same problem with 24x24 ones
The problem is when I run the program:
IT's clear that there is just one button as the toolbar button but I have inserted four buttons dynamically as you see in the code.
and the image is not shown even for this known button.
My code does not have any compiler or run-time error so I don't understand what is happening and whats the problem?
and since I'm new to MFC(this is my first program of MFC) I didn't know that adding a toolbar to a dialog-based application is such a hard task!!!! **
**So I created a dialog-based project at start and now that my program has been developed, I do need a toolbar
please help me. this is the forth question I have posted about adding a toolbar on a dialog based MFC application.
after writing the code to add a toolbar to a dialog-based mfc the dialog doesn't run
My toolbar on a dialog based mfc application is not shown
having trouble with LoadToolBarEx function of the CMFCToolBar class and set ID for the COpenGLControl class
But the problem has not been completely solved yet?
**Please introduce me a good reference that has taught adding toolbars to the MFC dialogs step by step from the scratch and has been useful for yourself
Oh and if there's a need to my project, it is downloadable here
as #Edward Clements suggested I changed the code to this but nothing changed.
VERIFY(m_FirstToolBar.LoadBitmap(IDB_NewTbrBtn));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_NEWTBRBTN,1));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_PanTbrBtn));
m_FirstToolBar.InsertButton( CMFCToolBarButton( IDC_PANTBRBTN,2));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_ZoomInTbrBtn));
m_FirstToolBar.InsertButton( CMFCToolBarButton( IDC_ZOOMINTBRBTN,3));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_ZoomOutTbrBtn));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_ZOOMOUTTBRBTN,4));
Firstly, according to the MFC sources, LoadBitmap() [NOT LoadBitmapW(), that seems to happen because of VS Intellisense picking up a #define from WinUser.h] adds the bitmap to the image list, so calling m_FirstToolBar.GetImages()->Load() will load the image twice.
Secondly, the InsertButton() should specify the index of the image of the button, m_FirstToolBar.GetImages()->GetCount() will always point to an invalid index value.
VERIFY(m_FirstToolBar.LoadBitmap(IDB_NewTbrBtn));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_PanTbrBtn));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_ZoomInTbrBtn));
VERIFY(m_FirstToolBar.LoadBitmap(IDB_ZoomOutTbrBtn));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_NEWTBRBTN, 0));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_PANTBRBTN, 1));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_ZOOMINTBRBTN, 2));
m_FirstToolBar.InsertButton(CMFCToolBarButton(IDC_ZOOMOUTTBRBTN, 3));
Related
Starting with October 2018 Update (version 1809) Win10 has support for Dark theme in Windows Explorer.
It can be configured here:
UI: Desktop | Context Menu | Personalize | Colors | Choose your default app mode = Dark
Registry : HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme = DWORD:0
While this setting exists for a while now, it only affected UWP applications. However, with this Windows 10 release, it also affects Windows Explorer, which is a Desktop application. This means that Windows now have internal support for it. Still, Desktop applications other then Windows Explorer are not affected at the moment.
I'd like to use it in my application. How is it implemented under the hood? Is there some way (manifest, WINAPI, etc) to subscribe for new dark theme?
Update 1:
I noticed that Windows Explorer Control Panel is partially light and partially dark, so it should be a per-window setting, rather then per-process setting.
One other example: Open File dialogs become dark in all Desktop applications, while the application itself remains in old light theme.
Update 2:
I tried SetWindowTheme(hwnd, L"Explorer", NULL); for TreeView and ListView. This visibly changes TreeView style (+ expand button becomes V), but the window remains white.
See https://github.com/ysc3839/win32-darkmode
This guy has it all laid out in some nice reusable code (MIT license).
It seems that Dark Mode is still an area of development in Windows 10, but I believe that Microsoft will eventually properly document and expose it to desktop apps.
Until then, we are stuck with undocumented ordinal-only imports, then custom draw and WM_CTLCOLOR* messages to dictate how controls that don't yet have native Dark Mode support are painted.
The most fundamental of the new Windows APIs are SetPreferredAppMode (uxtheme#135), to be called prior to any window creation, and AllowDarkModeForWindow (uxtheme#133), to be called on any Window that intends to use native Windows 10 dark mode support.
Here is the full list ordinal-only imports from that project:
using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build);
// 1809 17763
using fnShouldAppsUseDarkMode = bool (WINAPI *)(); // ordinal 132
using fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow); // ordinal 133
using fnAllowDarkModeForApp = bool (WINAPI *)(bool allow); // ordinal 135, removed since 18334
using fnFlushMenuThemes = void (WINAPI *)(); // ordinal 136
using fnRefreshImmersiveColorPolicyState = void (WINAPI *)(); // ordinal 104
using fnIsDarkModeAllowedForWindow = bool (WINAPI *)(HWND hWnd); // ordinal 137
using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI *)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106
using fnOpenNcThemeData = HTHEME(WINAPI *)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49
// Insider 18290
using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138
// Insider 18334
using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, since 18334
using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139
InitDarkMode imports and initializes dark mode, in a safe manner, carefully checking for min and max supported Windows 10 builds:
void InitDarkMode()
{
fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));
if (RtlGetNtVersionNumbers)
{
DWORD major, minor;
RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);
g_buildNumber &= ~0xF0000000;
if (major == 10 && minor == 0 && 17763 <= g_buildNumber && g_buildNumber <= 18363) // Windows 10 1809 10.0.17763 - 1909 10.0.18363
{
HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hUxtheme)
{
_OpenNcThemeData = reinterpret_cast<fnOpenNcThemeData>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49)));
_RefreshImmersiveColorPolicyState = reinterpret_cast<fnRefreshImmersiveColorPolicyState>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104)));
_GetIsImmersiveColorUsingHighContrast = reinterpret_cast<fnGetIsImmersiveColorUsingHighContrast>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106)));
_ShouldAppsUseDarkMode = reinterpret_cast<fnShouldAppsUseDarkMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132)));
_AllowDarkModeForWindow = reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));
auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));
if (g_buildNumber < 18334)
_AllowDarkModeForApp = reinterpret_cast<fnAllowDarkModeForApp>(ord135);
else
_SetPreferredAppMode = reinterpret_cast<fnSetPreferredAppMode>(ord135);
//_FlushMenuThemes = reinterpret_cast<fnFlushMenuThemes>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));
_IsDarkModeAllowedForWindow = reinterpret_cast<fnIsDarkModeAllowedForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));
if (_OpenNcThemeData &&
_RefreshImmersiveColorPolicyState &&
_ShouldAppsUseDarkMode &&
_AllowDarkModeForWindow &&
(_AllowDarkModeForApp || _SetPreferredAppMode) &&
//_FlushMenuThemes &&
_IsDarkModeAllowedForWindow)
{
g_darkModeSupported = true;
AllowDarkModeForApp(true);
_RefreshImmersiveColorPolicyState();
g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
FixDarkScrollBar();
}
}
}
}
}
Elsewhere, he takes advantage of WM_CTLCOLOR* messages and custom draw notifications to paint dark where Windows doesn't (yet) do it for us.
Note the FixDarkScrollBar. That is an IAT hook on OpenNcThemeData to over-ride the scrollbar theme selection by the listview class in comctl32. That is the part that bothers me most and I'm looking to axe it. I'm sure he is too.
I've adapted this code to my own application and it works well. I am, however, uncomfortable using these undocumented ordinal-only APIs (even as safely as possible), and fully expect Microsoft to eventually announce and document dark mode for Win32 apps and make this work redundant.
After some digging, I was able to find these two approaches. Both are undocumented and may change without notice.
1
SetWindowTheme(hwnd, L"DarkMode_Explorer", NULL);
2
using TYPE_AllowDarkModeForWindow = bool (WINAPI *)(HWND a_HWND, bool a_Allow);
static const TYPE_AllowDarkModeForWindow AllowDarkModeForWindow = (TYPE_AllowDarkModeForWindow)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133));
AllowDarkModeForWindow(a_HWND, true);
SetWindowTheme(hwnd, L"Explorer", NULL);
WARNING: Ordinal 133 may have completely different API behind it on other versions of Windows, including newer/older Win10 builds.
Both approaches apply some effects, but not everything.
For example, TreeView gets dark scrollbars and dark background for selected item, but the rest of background stays default.
Unfortunately, so far it's not like "call a function and that's it". It seems that even with correct theme applied, some background colors need to be handled manually.
I'm trying to show a window in xcb, inside the main window, but actually without luck.
The idea is that when the user press a button (in that case the X button) a small white window is shown (just for test).
But actually i'm stuck on that step. I watched the example code here:
http://en.wikibooks.org/wiki/X_Window_Programming/XCB
And tried to do the same in my application.
[EDIT 28/10/2013] Now with that code i can show a window, but if i try to add other variable like int i=0, or whatever, the window doesn't appear, and no expose events were raised (all events that were raised are or 0 or 2 (even if i add the variables inside other events). Any idea?
This is the XCB_KEY_PRESS event handler code:
Edit (with the new code)
case XCB_KEY_PRESS:{
xcb_key_press_event_t *kp = (xcb_key_press_event_t *)ev;
if(kp->detail==53){
printf("X pressed\n");
uint32_t vals[2];
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
vals[0]=screen->white_pixel;
vals[1]=XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
win = xcb_generate_id(connection);
xcb_create_window(
connection,
XCB_COPY_FROM_PARENT,
win,
root,
80,80,
150,150,
10,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, values);
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
vals[0]=screen->white_pixel;
vals[1]=0;
background=xcb_generate_id(connection);
xcb_create_gc(connection, background, win, mask, vals);
xcb_map_window(connection,win);
xcb_flush(connection);
printf("finished\n");
}
printf("KEY_PRESS - Pressed: %d\n", kp->detail);
}
root is the root window obtained from xcb_screen_t variable.
The definition of background and win are the following:
xcb_window_t win;
xcb_gcontext_t background;
And i added even a XCB_EXPOSE event handler:
case XCB_EXPOSE:{
printf("EXPOSE NEW WINDOW CREATED\n");
xcb_poly_fill_rectangle(connection, win, background,1,&rectangle);
xcb_flush(connection);
}
What is wrong with that code? What am i missing? (I'm trying to develop a very basic window manager, just for fun)
(My idea for that program is that when x is pressed an input box is shown, do you have any suggestion on how to do that?)
The following code opens a window of the right size, w,h, but not at the correct position, x,y.
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
using namespace std;
int main(int argc, char* argv[]){
Display *display; // A connection to X server
int screen_number;
Window canvas_window;
unsigned long white_pixel;
unsigned long black_pixel;
display = XOpenDisplay(NULL); // Connect X server by opening a display
if(!display){
cerr<<"Unable to connect X server\n";
return 1;
}
screen_number = DefaultScreen(display);
white_pixel = WhitePixel(display, screen_number);
black_pixel = BlackPixel(display, screen_number);
int x=0, y=100, w=300, h=400;
canvas_window = XCreateSimpleWindow(display,
RootWindow(display, screen_number),
x,y, // top left corner
w,h, // width and height
2, // border width
black_pixel,
white_pixel);
XMapWindow(display, canvas_window); // Map canvas window to display
XSync(display, False);
cin >> x; // just so that the program does not end
}
I compiled this with g++ xwindowtest.cpp -lX11 where g++ is version 4.6.2 and ran under Debian GNU/Linux.
The above solution is sort of correct, but not complete.
Creating a new top-level window on the desktop, and creating a new (child) window within the top-level window of your application use the same XCreateSimpleWindow() call, but the actual behaviour can be different.
When creating a new child window within your application you are in charge, and the origin coordinates (relative to its parent window's origin) and size you give for the new window will be honoured. In other words the window will go where you want it to.
However when creating a new top-level window on the desktop you have to deal with the pesky window manager, for example Motif, KDE, Gnome, etc. This intervenes when you create a top-level window to add borders ("decoration"), title, possibly icons, etc. More to the point it will, by default, ignore your requested origin coordinates in most cases and put the new window where it wants rather than where you asked it to put it. It is only when it has been mapped (somewhere) that you can then move it with XMoveWindow().
To avoid this you can ask, or in X11-speak "Hint to", the Window manager that "no, I want you to put the window where I ask, not where you want to put it". You do this with the following sequence:
(1) Define a XSizeHints structure.
(2) Set the flags bit in this structure with a mask of what you want to specify
(3) Populate the relevant arguments
(4) Call XSetNormalHints() on the newly created window (before you map it).
So in C code you would do:
XSizeHints my_hints = {0};
my_hints.flags = PPosition | PSize; /* I want to specify position and size */
my_hints.x = wanted_x_origin; /* The origin and size coords I want */
my_hints.y = wanted_y_origin;
my_hints.width = wanted_width;
my_hints.height = wanted_height;
XSetNormalHints(disp, new_window, &my_hints); /* Where new_window is the new window */
Then map it and - hopefully - it will be where you want it.
I usually declare a XSizeHints first and assign x,y coordinates etc to hints.
When creating x window you could do
XCreateSimpleWindow(display,
DefaultRootWindow(display),
hints.x, hints.y,
hints.width,hints.height,
borderWidth,
blackPixel, whitePixel)
That always works for me with 100% correct windows location.
I had the same problem. I am just starting with X11. Maybe others with more experience can clarify why this works (and simply specifying the x, y for XCreateSimpleWindow does not).
Here's my fix:
disp is your display, win0 is your canvas_window:
XMoveWindow(disp, win0, 200, 200);
XSync (disp, FALSE);
..... do something .....
XMoveWindow(disp, win0, 0, 0);
XSync (disp, FALSE);
... do something
When I run this code snippet, the window moves. You need to follow the XMoveWindow by XSync so that requests (such as a move) are handled appropriately.
I was wondering if there is a way to add (programatically, of course) an icon/button/whatever besides plain text to a window (Microsoft Windows window...)'s title bar or next to where the minimize/maximize/close buttons are. I could draw it myself and create an illusion it is a part of the window, but I wonder if in the user32 api there is such a method.
So far, I found a way to disable the minimize/maximize/close buttons but not a way to add a custom one to them. It seems strange to me.
Here is what I am trying to achieve:
I have been wondering how it is done here, since drawing a button for every window using gdi/gdi+ and then detecting if it is overlapped by another window and then displaying only the non-overlapped part seems to me like an unlikely solution. Probably the button has been registered in the window class so that every window has this button. Any pointers what to do?
In addition, how do I create a button at all, assuming I DON'T have Unicode enabled. Then in the following piece of code:
HWND hwndCommandLink = CreateWindow(
L"BUTTON", // Class; Unicode assumed.
L"", // Text will be defined later.
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_COMMANDLINK, // Styles.
10, // x position.
10, // y position.
100, // Button width.
100, // Button height.
hDlg, // Parent window.
NULL, // No menu.
(HINSTANCE)GetWindowLong(hDlg, GWL_HINSTANCE),
NULL); // Pointer not needed.
SendMessage(clHwnd, WM_SETTEXT, 0, (LPARAM)L"Command link");
SendMessage(clHwnd, BCM_SETNOTE, 0, (LPARAM)L"with note");
I have to substitute all the nice Windows constants with their long equivalent....However, when I search for them, all i get is this:
http://msdn.microsoft.com/en-us/library/bb775951(v=VS.85).aspx
Any pointers?
I need to do something which i expected to be was simple - create a tab control which has 2 tabs, implying 2 modes of operation for my app. When user clicks on Tab1, he'll be presented with some buttons and textboxes, and when he clicks Tab2, some other input method. I noticed that there was a CTabCtrl class thats used in MFC to add tabs.
However, once I added the tab ctrl using the UI designer, I couldn't specify how many tabs there'll be using property window. Searching on the net, I found some examples but all of them required you to derive from CtabCtrl , create 2 or more child dialogs etc and to write your own custom class. My question is, since I want to do something so basic, why couldn't I do it using the familiar Add Event handler/Add member variable wizard and then handle everything else inside my app's class ? Surely, the default CTabCtrl class can do something useful without needing to derive from it ?
Forget about CTabCtrl and use CMFCTabCtrl which is much easier to work with (this is assuming you are working on VS2008 SP1).
Failing that, you seem to misunderstand how the tab control works. It only provides the 'tab strip' at the top and sends messages when the user clicks on another one. It doesn't provide you with 'tab canvases' on which you can put controls. Showing and hiding the controls on the tab is something that the programmer needs to take care of. The resource editor provides little support there. Like Stewart says, the most common way of working is to have child dialogs in your tab and hide all of them except the one of the current tab.
You don't need to derive from CTabCtrl, you can also implement the switching behavior in the window that is the parent of the CTabCtrl.
The MFC tab control is a pretty thin wrapper over the win32 tab control, which works in pretty much the way you describe. It is a window, which provides switching between child windows using tabs. As it happens, in straight win32 this is the most useful way for it to work. If you want to do something more sophisticated than switching between individual windows, you do this by using child dialogs. MFC doesn't do a great deal to help you, but deriving from CTabCtrl and using child dialogs is really not very difficult to do, although if you're used to the way WinForms does tab controls it does seem unnecessary.
If you want the tab control at the root of the dialog, with no other controls along side it, you might want to look at CPropertySheet (http://msdn.microsoft.com/en-us/library/d3fkt014(VS.80).aspx) which is probably simpler to use. Unless you want to use any of the wizard functionality you don't even need to derive from it - you just create a couple of child dialog classes, then in the place where you want to create the property sheet, make an object, add the pages to it and invoke it.
The approach I took with an MFC dialog that contained a CTabCtrl was to derive a small class to manage the tab control and used dialog templates to create the actual tab window contents.
This is still being worked on so the source code is not very clean however here are some pieces. For instance CTabCtrlDialog needs constructor and destructor in order to release object which may have been created.
In the resource file I have a dialog template with a tab control followed by three dialog templates for each of the different tab content windows inserted into the tab control. While the dialog displaying the tab control has the WS_POPUP style, the dialog templates for the tab windows that are inserted into the tab control have a WS_CHILD style instead. This change makes the tab windows child windows so that when the dialog is moved, everything stays lined up properly with no further effort on my part.
In my case the tab windows which are inserted into the tab control display a set of check boxes to indicate various operational parameters. Using a dialog template approach makes it very easy to create the necessary tab window content.
I derive a class from CTabCtrl that extends the standard MFC class with an additional method for inserting into the tab control a tab window based on a specified dialog template id. Since this is just a single dialog, I just put this class into the same files, .h and .cpp, as the dialog components themselves.
class CTabCtrlDialog : public CTabCtrl
{
public:
void InsertItemDialogTemplate (UINT nIDTemplate, int nItem, TCITEM* pTabCtrlItem);
public:
struct {
UINT nIDTemplate;
CDialog *pDialog;
} m_pDialogData[10];
};
The method InsertItemDialogTemplate() looks like:
/*
* InsertItemDialogTemplate ()
*
* Insert into a tab control a tab pane based on the specified dialog template. The
* dialog template describes what the tab pane looks like so far as controls, etc.
*
* NOTE: The STYLE description must be WS_CHILD and not WS_POPUP. Also the dialog
* needs to have as its top coordinate some distance in pixels so that the
* various tab descriptions are visible. For instance an example dialog
* template in the resource file may look like:
* IDD_CASHIER_TAB_ONE DIALOGEX 0, 10, 178, 113
* STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
* FONT 8, "MS Shell Dlg", 400, 0, 0x1
* BEGIN
* LTEXT "Dialog Tab one",IDC_STATIC,6,44,90,17
* END
*
**/
void CTabCtrlDialog::InsertItemDialogTemplate (UINT nIDTemplate, int nItem, TCITEM* pTabCtrlItem)
{
InsertItem (nItem, pTabCtrlItem);
m_pDialogData[nItem].nIDTemplate = nIDTemplate;
m_pDialogData[nItem].pDialog = new CDialog ();
m_pDialogData[nItem].pDialog->Create (nIDTemplate, this);
m_pDialogData[nItem].pDialog->ShowWindow (FALSE);
}
For handling tab selection which displays the various tabs I have the following message map and then the two event handlers in the dialog.
BEGIN_MESSAGE_MAP(CDiaCashierEdit, CDialog)
ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_CASHIER_EDIT_STATUS, &CDiaCashierEdit::OnTcnSelchangeTabCashierEditStatus)
ON_NOTIFY(TCN_SELCHANGING, IDC_TAB_CASHIER_EDIT_STATUS, &CDiaCashierEdit::OnTcnSelchangingTabCashierEditStatus)
END_MESSAGE_MAP()
void CDiaCashierEdit::OnTcnSelchangeTabCashierEditStatus(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
int i = TabCtrl_GetCurSel(pNMHDR->hwndFrom);
m_TabCtrl.m_pDialogData[i + 1].pDialog->ShowWindow (TRUE);
}
void CDiaCashierEdit::OnTcnSelchangingTabCashierEditStatus(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
int i = TabCtrl_GetCurSel(pNMHDR->hwndFrom);
m_TabCtrl.m_pDialogData[i + 1].pDialog->ShowWindow (FALSE);
}
In the DoDataExchange() method of the dialog I have the following which creates first the tab control and then creates each of the tab windows and inserts them into the tab control.
void CDiaCashierEdit::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_CASHIER_NAME, m_CashierName);
DDX_Control(pDX, IDC_EDIT_CASHIER_SUPNO, m_SupervisorId);
DDX_Control(pDX, IDC_EDIT_CASHIER_TEAMNO, m_TeamNumber);
DDX_Control(pDX, IDC_EDIT_CASHIER_GCSTART, m_GuestCheckStart);
DDX_Control(pDX, IDC_EDIT_CASHIER_GCEND, m_GuestCheckEnd);
DDX_Control(pDX, IDC_TAB_CASHIER_EDIT_STATUS, m_TabCtrl);
if (pDX->m_bSaveAndValidate) {
m_CashierName.GetWindowText (m_paraCashier.auchCashierName, 20);
m_paraCashier.usSupervisorID = m_SupervisorId.GetWindowTextAsInt();
m_paraCashier.uchTeamNo = m_TeamNumber.GetWindowTextAsInt();
m_paraCashier.usGstCheckStartNo = m_GuestCheckStart.GetWindowTextAsInt();
m_paraCashier.usGstCheckEndNo = m_GuestCheckEnd.GetWindowTextAsInt();
for (int i = 0; i < sizeof(m_TabItemOneStatus)/sizeof(m_TabItemOneStatus[0]); i++) {
int iTab = m_TabItemOneStatus[i].sTabItem;
int iDlg = m_TabItemOneStatus[i].iDlgItem;
int iOffset = m_TabItemOneStatus[i].sOffset;
CButton *p = (CButton *) m_TabCtrl.m_pDialogData[iTab].pDialog->GetDlgItem(iDlg);
if (p->GetCheck()) {
m_paraCashier.fbCashierStatus[iOffset] |= m_TabItemOneStatus[i].uchBit;
} else {
m_paraCashier.fbCashierStatus[iOffset] &= ~(m_TabItemOneStatus[i].uchBit);
}
}
} else {
m_CashierName.SetWindowText(m_paraCashier.auchCashierName);
m_SupervisorId.SetWindowTextAsInt (m_paraCashier.usSupervisorID);
m_TeamNumber.SetWindowTextAsInt (m_paraCashier.uchTeamNo);
m_GuestCheckStart.SetWindowTextAsInt (m_paraCashier.usGstCheckStartNo);
m_GuestCheckEnd.SetWindowTextAsInt (m_paraCashier.usGstCheckEndNo);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_ONE, 1, &m_TabItemOne);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_TWO, 2, &m_TabItemTwo);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_THREE, 3, &m_TabItemThree);
for (int i = 0; i < sizeof(m_TabItemOneStatus)/sizeof(m_TabItemOneStatus[0]); i++) {
int iTab = m_TabItemOneStatus[i].sTabItem;
int iDlg = m_TabItemOneStatus[i].iDlgItem;
int iOffset = m_TabItemOneStatus[i].sOffset;
CButton *p = (CButton *) m_TabCtrl.m_pDialogData[iTab].pDialog->GetDlgItem(iDlg);
if (m_paraCashier.fbCashierStatus[iOffset] & m_TabItemOneStatus[i].uchBit) {
p->SetCheck (1);
} else {
p->SetCheck (0);
}
}
m_TabCtrl.m_pDialogData[1].pDialog->ShowWindow (TRUE);
}
}