Win32 / WTL- My Checkbox imagelist state reverts to unchecked state - winapi

I'm creating a custom window in WTL to hold video controls for a DirectShow app.
I've created a set of check boxes that I wish to behave in the "push like" manner (BS_PUSHLIKE). They appear defined in the .rc file for the dialog like so:
CONTROL "",IDC_VID1,"Button",BS_AUTOCHECKBOX | BS_BITMAP | BS_PUSHLIKE | WS_GROUP | WS_TABSTOP,6,7,19,18
I've also loaded an image list for the button, which correctly shows the appropriate image when the button is in LButtonDown and LButtonUp states:
In the code below, m_btnVid1 is defined as a WTL::CBitmapButton
m_ilBtnVid1.CreateFromImage( IDB_TV, 25, 1, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION );
m_btnVid1.SubclassWindow( (HWND) ::GetDlgItem(m_hWnd, IDC_VID1) );
m_btnVid1.SetImageList( m_ilBtnVid1.m_hImageList );
m_btnVid1.SetImages(0,1);
As soon as I release the mouse button, the checkboxes state reverts to "unchecked" state.
I've added a COMMAND_ID_HANDLER_EX to the IDC_VID1 control to determine the state of the control, but the nState variable always reads 0:
void OnVid1(UINT uNotifyCode, int nID, CWindow wndCtl)
{
CBitmapButton* btn = &m_btnVid1;
int nState = btn->GetCheck();
switch( nState )
{
case BST_UNCHECKED:
btn->SetCheck( BST_CHECKED );
nState = btn->GetCheck();
break;
default:
btn->SetCheck( BST_UNCHECKED );
break;
}
}
Can anyone suggest why this might be - am I missing a creation style in the CBitmapButton?
If it helps, I'm sure I can post the VC2008 solution file online somehow.

Related

UAC shield icon (BCM_SETSHIELD) not on a button

Following code adds a shield icon to a button:
SendMessage(btn1.Handle, BCM_SETSHIELD, 0, 1);
I've tried the same with checkboxes, radio button groups etc. Nothing works except a button.
Is there a way to add the shield icon to any other control?
Is there a way to add the shield icon to any other control?
If the control in question supports the display of a HICON (e. g. a static control), you can load the standard shield icon by calling LoadIconWithScaleDown with the IDI_SHIELD constant and assign that to the control.
int cx = GetSystemMetrics( SM_CXSMICON );
int cy = GetSystemMetrics( SM_CYSMICON );
HICON hShieldIcon = NULL;
HRESULT hr = LoadIconWithScaleDown( NULL, MAKEINTRESOURCE( IDI_SHIELD ), cx, cy,
&hShieldIcon );
if( SUCCEEDED( hr ) )
{
// Consult the reference on how to assign the HICON to the control.
}
If the control in question does not support assigning a HICON, you could use custom-draw, which is supported by many controls, to draw the icon yourself.
The documentation says:
Sets the elevation required state for a specified button or command link to display an elevated icon.
This implies that the only supported controls are buttons and command links.

Mirrored text when changing a combobox RTL style

I'm trying to create a dynamic dialog, which can be made RTL depending on the language. But I have the following issue: whenever I change the RTL style of the combo box, the text comes up reversed. I tried using functions such as InvalidateRect, RedrawWindow, etc., but couldn't make it work correctly.
Relevant code (WinAPI with WTL):
CComboBox combo = hWndCtl;
if(combo.GetCurSel() == 0)
combo.ModifyStyleEx(WS_EX_LAYOUTRTL, 0);
else
combo.ModifyStyleEx(0, WS_EX_LAYOUTRTL);
A demo project: here.
A demonstration of the issue:
It seems you are responding to CBN_SELCHANGE notification. This is notification is sent after combobox sets the text in its editbox.
You should respond to CBN_SELENDOK instead. CBN_SELENDOK is sent before CBN_SELCHANGE, this gives you time to modify the style before combobox sets the text.
switch (HIWORD(wParam))
{
case CBN_SELENDOK:// CBN_SELCHANGE:
if (SendMessage(hComboBox, CB_GETCURSEL, 0, 0) == 0)
ModifyStyleEx(hComboBox, WS_EX_LAYOUTRTL, 0);
else
ModifyStyleEx(hComboBox, 0, WS_EX_LAYOUTRTL);
break;
default:break;
}
Edit: Windows 10 has fade in/out effect. If you change the combo selection with keyboard, while the color is fading, the text is still goes backward.
ComboBox has an edit control which might be causing this problem. It's better to use WS_EX_RIGHT | WS_EX_RTLREADING instead of WS_EX_LAYOUTRTL. This will also work with CBN_SELCHANGE.
case CBN_SELENDOK: //(or CBN_SELCHANGE)
if (SendMessage(hComboBox, CB_GETCURSEL, 0, 0) == 0)
ModifyStyleEx(hComboBox, WS_EX_RIGHT | WS_EX_RTLREADING, 0);
else
ModifyStyleEx(hComboBox, 0, WS_EX_RIGHT | WS_EX_RTLREADING);
break;

Can't change tooltip coordinates MFC

I need to make tooltip a little bit right and lower to mouse cursor, but i can't do it in any way, tried different coordintaes but nothing seems to work. Where is the problem? Thank you.
// Add the new tooltip (if available)
if (m_LastToolTipRow!=-1 && m_LastToolTipRow!=-1)
{
// Not using CToolTipCtrl::AddTool() because it redirects the messages to CListCtrl parent
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
m_OwnToolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
m_OwnToolTipCtrl.SendMessage(TTM_TRACKPOSITION, 0, (LPARAM)MAKELPARAM(pt.x + 100, pt.y + 100));
m_OwnToolTipCtrl.SendMessage(TTM_TRACKACTIVATE, true, (LPARAM)&ti);
m_OwnToolTipCtrl.Activate(TRUE);
//Multiline
m_OwnToolTipCtrl.SetMaxTipWidth(256);
//m_OwnToolTipCtrl.SetMaxTipWidth(SHRT_MAX);
}
TTF_IDISHWND
Indicates that the uId member is the window handle to the tool. If this flag is not set, uId is the tool's identifier.
According to this, the window with m_hWnd handle is the one that shows the tooltip and you can position the window itself. If you meant a tooltip separate to that window than there is a principal problem there.

Deactivating desktop background when a pop up is shown to user

I have a win32 application that runs full screen when started. The application has some button which invoke pop up dialogs.
Is there a way to make the entire desktop (except the pop up) go transparent black unless the pop up is dismissed by the user? what I am talking of is similar to windows 7 UAC pop ups and the background it causes.
Is it possible to do similar stuff for a full screened window app?
It is possible do this…sort of. Perhaps I should say, you can simulate this effect. It won't actually be like the UAC dialog, as the user will still be able to interact with other running applications. There is no such concept as "system modal" available to applications. That's by design, of course. But you can certainly show a "light box" that dims out the rest of the desktop and forces focus on your app's dialog box.
The way I would do it is to create a giant layered window that sits on top of all other windows and covers the entire screen, fill it with black, and set the opacity as desired. Then, before you show a modal dialog (either by calling the MessageBox function or using the DialogBox function to show one of your own custom dialogs), display your light box window. Finally, after the user dismisses the modal dialog, you will destroy the light box window.
Here's some sample code. Error checking is omitted for brevity. So is other good style, like wrapping this mess up in one or more classes.
INT_PTR ShowLightBoxedDialog(HINSTANCE hInstance,
LPCTSTR pDlgTemplate,
HWND hwndParent,
DLGPROC pDlgProc,
BYTE opacityLevel)
{
const TCHAR szLightBoxClass[] = TEXT("LightBoxWndClass");
// Register the light box window class.
static bool lightBoxRegistered = false;
if (!lightBoxRegistered)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = CS_NOCLOSE | CS_SAVEBITS;
wcex.lpfnWndProc = LightBoxWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hIconSm = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szLightBoxClass;
RegisterClassEx(&wcex);
lightBoxRegistered = true;
}
// Create and display the light box window.
HWND hwndLightBox = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_LAYERED,
szLightBoxClass,
NULL,
WS_POPUPWINDOW,
0, 0, 0, 0,
hwndParent,
NULL,
hInstance,
NULL);
SetLayeredWindowAttributes(hwndLightBox, 0, opacityLevel, LWA_ALPHA);
SetWindowPos(hwndLightBox,
HWND_TOP,
GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN),
SWP_SHOWWINDOW);
// Display the modal dialog box (as you normally would).
// NOTE: The dialog box won't appear centered on the screen.
// For that, you will need to write centering code in response
// to the WM_INITDIALOG message in the dialog procedure.
INT_PTR result = DialogBox(hInstance, pDlgTemplate, hwndLightBox, pDlgProc);
//
// For demonstration purposes, I used the following code:
// INT_PTR result = MessageBox(hwndLightBox,
// TEXT("OH NOEZ!\n\nYour system is kaput! Abandon þe all hope."),
// NULL,
// MB_ABORTRETRYIGNORE | MB_ICONERROR);
// Destroy the light box window.
DestroyWindow(hwndLightBox);
// Return the result of the modal dialog box.
return result;
}
You'll notice that basically what I've done is created a wrapper around the DialogBox function, which you use whenever you want a dialog box with a "light box" effect. It takes all of the same parameters (the first 4), and then there's an additional one tacked on the end that allows you to specify the opacity level used for the "light box" effect. Something in the range of 150–200 is probably good. Naturally, you could pick something and hard-code it, but I suffer from severe allergies to hard-coded values. Anyway, it's super easy to call this function from anywhere:
ShowLightBoxedDialog(hInstance, /* your application instance */
MAKEINTRESOURCE(IDD_SAMPLE), /* your dialog template */
hWnd, /* parent window for dialog */
SampleWndProc, /* ptr to dialog procedure */
175); /* light box opacity level */
Because the code takes advantage of how modal dialogs already work in Windows, the user won't be able to interact with any other pieces of your application until they dismiss the dialog box. And because the "light box" window is positioned on top of everything else, it eats all mouse clicks and prevents setting focus to any other application. But it is trivial to work around using something like Alt+Tab.
So this is not a security feature! It is merely a visual effect!
And because it's just a silly visual effect, it's likely to be a frustrating one for your users. I don't actually recommend using it. But now you know how to do it. Wield such power responsibly, etc.

How to use a CTabCtrl in a MFC dialog based application?

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);
}
}

Resources