CToolTipCtrl with TTF_TRACK Flag won't disappear, SetDelayTime doesn't work? - windows

I found if set TTF_TRACK flag to CTooltipCtrl, SetDelayTime doesn't work anymore, so the tooltip won't disappear automatically. I want to show the tooltip just below specified control, and expect it to disappear after a few seconds(the time can set with SetDelayTime).
Anybody can help me?
Here is my steps to construct tool tip:
1. Add member variable
CToolTipCtrl m_tooltip;
2. override PreTranslateMessage
BOOL CPF_GetSetNameDlg::PreTranslateMessage( MSG* pMsg )
{
switch (pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
m_tooltip.RelayEvent(pMsg);
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
3. OnInitialDialog
BOOL CPF_GetSetNameDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//tooltip
EnableToolTips();
m_tooltip.Create(this , WS_POPUP | TTS_NOPREFIX | TTS_BALLOON);
m_tooltip.SetDelayTime(TTDT_INITIAL, 0);
m_tooltip.SetDelayTime(TTDT_AUTOPOP, 30000);
m_tooltip.SetDelayTime(TTDT_RESHOW, 30000);
m_tooltip.AddTool(GetDlgItem(IDC_SETNAME), _T(""));
m_tooltip.SetMaxTipWidth(600);
}
4. Control to show tool tip
if(bShow)
{
m_tooltip.UpdateTipText(_T("Hello, money~"), pWnd);
CToolInfo sTinfo;
m_tooltip.GetToolInfo(sTinfo, pWnd);
sTinfo.uFlags = TTF_TRACK;
m_tooltip.SetToolInfo(&sTinfo);
CRect rect;
pWnd->GetWindowRect(rect);
m_tooltip.SendMessage(TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rect.left, rect.bottom));
m_tooltip.SendMessage(TTM_TRACKACTIVATE, TRUE, (LPARAM)&sTinfo );
}

If you use tracking tooltips then you are responsible for showing and hiding the tooltip manually. The tooltip timers (like "autopop") are ignored. So you need to create your own timer and hide the tooltip yourself when it fires.

Related

How do I make a combobox show a tab control like VS' when setting a color?

I mean this control:
When you click on this, instead of regular options, a tab control with the colors is displayed. How can I do this? is this a owner-draw combobox or something else? I'm aware on how draw text, rectangles, images, etc with a owner-draw combobox but I don't know how add controls over there. I have no code to show yet because I have no idea how do that. I've tried something like call CreateWindow() in WM_DRAWITEM using the values from DRAWITEMSTRUCT.rcItem but I can't make a control inside the groupbox's client area, the button gets behind the control.
Looks like you are looking for CBN_DROPDOWN.
Sent when the list box of a combo box is about to be made visible. The
parent window of the combo box receives this notification code through
the WM_COMMAND message.
Some code:
HWND hWndComboBox = CreateWindow(
WC_COMBOBOX,
TEXT(""),
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
10, 20, 70, 17,
hWnd, (HMENU)IDB_COMBOX, hInstance, NULL);
...
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDB_COMBOX:
{
switch (HIWORD(wParam))
{
case CBN_DROPDOWN:
{
CHOOSECOLOR cc; // common dialog box structure
static COLORREF acrCustClr[16]; // array of custom colors
static DWORD rgbCurrent; // initial color selection
// Initialize CHOOSECOLOR
ZeroMemory(&cc, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = hWnd;
cc.lpCustColors = (LPDWORD)acrCustClr;
cc.rgbResult = rgbCurrent;
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
ChooseColor(&cc);
}
break;
...
Debug:

How to check if a button is checkbox on 64 bit windows?

I'm checking if a button is checkbox of 32bit process on 64bit windows10.
The problem is that I can not distingush checkbox from normal button.
The buttons are different in Window-Detective:
(After I restart the application, even Window-Detective shows it is a button now!)
But the checkbox can't be recognized as checkbox in Spy++
BS_CHECKBOX is not listed.
Code (compiled as 32bit):
TEST_METHOD(ShouldCheckStyle) {
auto styleOfButton = ::GetWindowLongPtr((HWND)0x003F06E8, GWL_STYLE);
auto styleOfCheckbox = ::GetWindowLongPtr((HWND)0x01101642, GWL_STYLE);
auto bsOfButton = styleOfButton & BS_TYPEMASK;
auto bsOfCheckbox = styleOfCheckbox & BS_TYPEMASK;
auto resultOfButton = (bsOfButton == BS_CHECKBOX);
auto resultOfCheckbox = (bsOfCheckbox == BS_CHECKBOX);
auto debugger = 0;
}
Debug output
The code indicates they both have BS_OWNERDRAW. The above behaves the same for the button and the checkbox.
The weird thing is Window-Detective can recognize the style of checkbox. The code is same as I used above. Here's a piece of code:
Window* WindowManager::createWindow(HWND handle) {
WindowClass* windowClass = getWindowClassFor(handle);
String className = windowClass->getName().toLower();
if (className == "button") {
LONG typeStyle = GetWindowLong(handle, GWL_STYLE) & BS_TYPEMASK;
switch (typeStyle) {
case BS_CHECKBOX:
case BS_AUTOCHECKBOX:
case BS_3STATE:
case BS_AUTO3STATE: {
return new CheckBox(handle, windowClass);
}
case BS_RADIOBUTTON:
case BS_AUTORADIOBUTTON: {
return new RadioButton(handle, windowClass);
}
case BS_GROUPBOX: {
return new GroupBox(handle, windowClass);
}
default: {
// If none of the above is true, then the control is just a Button
return new Button(handle, windowClass);
}
}
}
After some discussion, you can use GetWindowText to get the text from each control and compare the specific text.
BS_CHECKBOX cannot be detected from the properties of the "checkbox" control beacuse of BS_OWNERDRAW.
Creates an owner-drawn button. The owner window receives a WM_DRAWITEM
message when a visual aspect of the button has changed. Do not combine
the BS_OWNERDRAW style with any other button styles.
Try the below code:
WCHAR str1[20];
WCHAR str2[] = L"Agree me";
GetWindowText(hwnd_checkbox, str1, 256);
if (_tcscmp(str1, str2) == 0)
{
//it is checkbox
}
else
{
//it isn't checkbox
}
After you get the correct control handle of the checkbox, you can use SendDlgItemMessage or SendMessage to send BM_SETCHECK check message.
SendMessage(hwnd_checkbox, BM_SETCHECK, BST_CHECKED, 0);

Forcing a combobox to "dropdown" above instead of below

When you click on the "dropdown" button of a combobox, the dropped down listbox appears below the combobox, unless there is not enough space below, in which case the listbox appears above.
Now I wonder if there is a possibility to force the lisbox to appear above the combobox, even if there is enough space below.
Illustration
When I click on the combo box, I'd like the "drop down" list box appear always above as on the left screen copy.
Everything is possible, and you don't need to implement the control "from scratch".
First, you can subclass the ListBox part of your ComboBox to get complete control over it, as explained in MSDN. You can create a class, derived from CListBox, using the Class Wizard. You only need to implement WM_WINPOSITIONCHANGING handler in it:
void CTopListBox::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
CListBox::OnWindowPosChanging(lpwndpos);
if ((lpwndpos->flags & SWP_NOMOVE) == 0)
{
lpwndpos->y -= lpwndpos->cy + 30;
}
}
Here, for simplicity, I am moving the box up by the (heights+30). You can get the height of your ComboBox instead of my 30.
Then you declare a member variable in your dialog class:
CTopListBox m_listbox;
and subclass it like that:
HBRUSH CMFCDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (nCtlColor == CTLCOLOR_LISTBOX)
{
if (m_listbox.GetSafeHwnd() == NULL)
{
m_listbox.SubclassWindow(pWnd->GetSafeHwnd());
CRect r;
m_listbox.GetWindowRect(r);
m_listbox.MoveWindow(r);
}
}
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
return hbr;
}
Note that I am calling m_listbox.MoveWindow(r) there; it is needed because first WM_CONTROLCOLOR message for that list box comes after it is positioned, so the very first time it would drop down instead of up.
Disclaimer: this is not a very clean solution, as, if you have windows animation enabled, you'd see that the list unrolls from top to bottom.
Alternatively, you should be able to "fool" the combobox that it is too close to the bottom of the screen; then it will drop up by itself. I leave it as an exercise for the readers :)
This would be relatively easy except when combo box has "slide open" effect. If you move the dropdown listbox to the top, and the combo slides open from top-to-bottom, it would look odd. So you have to disable the animation or reverse it.
In this function I call AnimateWindow in OnWindowPosChanging, it doesn't seem to cause any problems but I am not a 100% sure about it!
class CComboBox_ListBox : public CListBox
{
public:
CWnd *comboBox;
void OnWindowPosChanging(WINDOWPOS *wndpos)
{
CListBox::OnWindowPosChanging(wndpos);
if (comboBox && wndpos->cx && wndpos->cy && !(wndpos->flags & SWP_NOMOVE))
{
CRect rc;
comboBox->GetWindowRect(&rc);
//if listbox is at the bottom...
if (wndpos->y > rc.top) {
//if there is enough room for listbox to go on top...
if (rc.top > wndpos->cy) {
wndpos->y = rc.top - wndpos->cy;
BOOL animation;
SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &animation, 0);
//if combobox slides open...
if (animation) {
//we have to set the x coordinate otherwise listbox
//is in the wrong place when parent window moves
SetWindowPos(0, wndpos->x, wndpos->y, 0, 0,
SWP_NOSENDCHANGING | SWP_HIDEWINDOW | SWP_NOSIZE);
AnimateWindow(100, AW_VER_NEGATIVE);
}
}
}
}
}
DECLARE_MESSAGE_MAP()
};
Usage:
COMBOBOXINFO ci = { sizeof(COMBOBOXINFO) };
comboBox.GetComboBoxInfo(&ci);
CComboBox_ListBox *listBox = new CComboBox_ListBox;
listBox->comboBox = &comboBox;
listBox->SubclassWindow(ci.hwndList);
Also you can use SetMinVisibleItems to reduce the listbox height and make sure the dropdown list fits on top.

CListCtrl set font style to bold

I want to change font of any cell of CListCtrl control to bold. Can any one tell how to do it for CList Ctrl.
I have already done this for a CTreeCtrl, like this
pTC->SetItemState(hItemCur, TVIS_BOLD, TVIS_BOLD);
do we have something similar for CListCtrl?
Thanks in advance.
If you can use CMFCListCtrl (VS2008 SP1 and up), you can derive a class from it and override OnGetCellFont. From there you return your bold font (you can create your own or return AFX_GLOBAL_DATA::fontBold):
HFONT CMyListCtrl::OnGetCellFont( int nRow, int nColumn, DWORD dwData /*= 0*/ )
{
if (UseBoldFont(/* params */))
{
return GetGlobalData()->fontBold;
}
return NULL;
}
If you have to stick to plain old CListCtrl, the easiest way would be to use Custom Draw, where you can tweak the drawing process to your own needs. Don't confuse it with Owner Draw, where you have to do all the drawing yourself.
Here's an article explaining the basics of using Custom Draw with CListCtrl.
Add
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdraw)
to your message map.
Now you can simply modify items as you wish in this function. In here you can change the align, font, background-color, text-color, [...], and you can set items to bold -> example. The best way IMO, is to either store a pointer to a struct, class or simply a flag in the LPARAM of the item(s) in the control. This function works for both CListCtrl and CTreeCtrl.
Here is an example with flags:
enum ColorFlags
{
F_COLOR_BLACK = 0x1,
F_COLOR_WHITE = 0x2
//and more...
};
enum CustomColors
{
COLOR_BLACK = RGB(0, 0, 0),
COLOR_WHITE = RGB(255, 255, 255)
};
afx_msg
void CMyListCtrl::OnCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVCUSTOMDRAW *pDraw = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
switch (pDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW; //Do not forget this...
break;
case CDDS_ITEMPREPAINT:
{
switch (pDraw->nmcd.lItemlParam) //Extract color from flags
{
case F_COLOR_BLACK:
{
pDraw->clrText = COLOR_BLACK;
} break;
case F_COLOR_WHITE:
{
pDraw->clrText = COLOR_WHITE;
} break;
default:
break;
} //switch
} break;
} //switch
}

Two dialogs in one MFC application

I am trying to make a dialog based MFC application , where two dialogs needs to be shown sequentially.
What that means is , once the first dialog(modal) is shown and dismissed (by pressing OK), the second dialog needs to be brought up.My requirement is the second dialog should be modeless.
But what I observe is the second dialog is shown but none of the message handling function are being called in response of user messages.I think the message map itself is not working, while the overridden functions(like OnInitdialog) are being called. I tried replacing this modeless dialog with a modal one , and alas, the doModal() itself fails.
Here is the little code:
CFirstDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
m_pMainWnd = dlgModeLess;
dlgModeLess->ShowWindow(SW_SHOW);
dlgModeLess->UpdateWindow();
}
Here is the message map of the second dialog:
BEGIN_MESSAGE_MAP(CSecondDlg, CDialog)
ON_MESSAGE(TRAY_MESSAGE,OnTrayMessage)
ON_BN_CLICKED(IDOK, &CSecongDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_RADIO1, &CSecondDlg::OnBnClickedRadio1)
END_MESSAGE_MAP()
I think I am doing something conceptually wrong. Kindly share your thoughts on what need to be done to tackle such a scenario.
As I mentioned in a previous post, it is not necessary that the second dialog be non modal.
Just do something like this:
BOOL CMyTestApp::InitInstance()
{
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
COtherDlg Dlg ;
m_pMainWnd = &dlg;
if (Dlg.DoModal() == IDCANCEL)
{
...
}
}
else if (nResponse == IDCANCEL)
{
...
}
return FALSE;
}
When you create a modeless dialog box, control will return to your calling function right away, so you need to declare variable dlgModeLess at a global scope and make sure your program/scope is still active until the dialog box finishes
I have solved this problem and this turned out to be interesting.
It seems that Cdialog::Create() itself is not sufficient for creating a fully operative modeless dialog box. We have to supply a win32 style message loop to it.
So this effectively makes two message loops in the program, one provide my MFC framework and the one that I wrote after returning from IDOK. Here is the modified code.
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
CTrayIconDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG leftmsg;
PeekMessage(&leftmsg,m_pMainWnd->m_hWnd,0,0,PM_REMOVE);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, dlgModeLess->m_hWnd, 0, 0)) != 0)
{
if (bRet == -1)
{
// Handle the error and possibly exit
}
else if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
This code works as expected, The interesting thing to note here is the PeekMessage function which removes a WM_QUIT message that is inserted in the thread's message queue once the first dialog is dismissed as we do not want to quite at that point of time. This i believe is done by the MFC framework.

Resources