MFC Win32 | LButtonUp not being received after clicking toolbar button (CMFCToolBarButton::OnClick) - c++

What I'm trying to achieve
Well, the title might not have explained the problem very well, so here goes:
I am trying to create a Win32 app using MFC that lets you edit and inspect other windows.
I want the user to be able to select other windows.
I got inspired by the "Find Window Process" tool on the toolbar on sysinternals applications such as ProcessExplorer.
The way it works is you click, then the window disappears, and then you drag it over the window you want to select. A border pops up around it and when you let go, it selects the window the mouse is over.
My problem
The problem I was facing is that I don't know how to detect when the user lets go of the mouse on another window.
I detect mouse down using OnClick in CMFCToolBarButton
I tried using SetCapture() but that did nothing.
I tried using OnNcLButtonUp and OnLButtonUp but neither of them worked. (alongside SetCapture)
Here's my code so far (ChildView.cpp):
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_UPDATE_COMMAND_UI(ID_TB_LOCATEWINDOW, &CChildView::EnableToolbarButton)
ON_UPDATE_COMMAND_UI(ID_TOOLS_MESSAGELAUNCHER, &CChildView::EnableToolbarButton)
ON_WM_XBUTTONUP()
// ON_WM_LBUTTONUP()
ON_WM_NCLBUTTONUP()
END_MESSAGE_MAP()
....
void CChildView::LocateWindow()
{
GetParentFrame()->ShowWindow(SW_MINIMIZE);
SetCapture();
}
void CChildView::OnNcLButtonUp(UINT nHitTest, CPoint point)
{
ReleaseCapture();
GetParentFrame()->ShowWindow(SW_NORMAL);
MessageBox(L"Stuff", L"");
CWnd::OnNcLButtonUp(nHitTest, point);
}
I want to mention that the LocateWindow function gets called when the toolbar button is clicked (as in mouse down, not mouse down AND up)
It is called from the OnClick function.
Here's the code for that:
(I replace the button with OnToolbarReset)
// CLocateWindowButton.cpp : implementation file
//
#include "pch.h"
#include "WindowHacker.h"
#include "MainFrm.h"
#include "CLocateWindowButton.h"
// CLocateWindowButton
IMPLEMENT_SERIAL(CLocateWindowButton, CMFCToolBarButton, 1)
// CLocateWindowButton member functions
CLocateWindowButton::CLocateWindowButton()
{
}
CLocateWindowButton::CLocateWindowButton(CMainFrame* mainFrame, UINT uiCmdID, LPCTSTR lpszText) : CMFCToolBarButton(uiCmdID, NULL, lpszText)
{
this->mainFrame = mainFrame;
}
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
return FALSE;
}
void CLocateWindowButton::CopyFrom(const CMFCToolBarButton& src)
{
CMFCToolBarButton::CopyFrom(src);
mainFrame = ((CLocateWindowButton&)src).mainFrame;
}
//void CLocateWindowButton::AssertValid() const
//{
// CMFCToolBarButton::AssertValid();
//
// // TODO: Add your specialized code here and/or call the base class
//}
UPDATE:
It seems to work when I put it inside an LButtonDown event, it just seems to not work when it is being detected from OnClick in CMFCToolBarButton

I found that in CMFCToolBar::OnLButtonUp, after calling OnClick in the button, it recaptures the cursor, invalidating our SetCapture.
BUT if I return TRUE instead of FALSE in OnClick, the mouse is not recaptured.
So changing this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return FALSE;
}
To this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return TRUE; // The line is changed here
}
The message gets sent to CMainFrame instead.

Related

How to activate a button (CButton) located in a disabled window (CWnd)?

I have this code :
m_pBtnCom = new CButton();
m_pBtnCom->Create(_T("Push"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_TEXT|BS_VCENTER|BS_CENTER, rc, this, BTN_CMT);
Where:
this = my derived CWnd class
rc = CRect button position
BTN_CMT = button id
Current context:
If I disable the parent CWnd by calling EnableWindow(FALSE), even if I call the function EnableWindow(TRUE) on the button (m_pBtnCom->EnableWindow(TRUE)), the latter remains disabled; Therefore, nothing works on it: click, tooltip, ...
I tried to remove WS_CHILD, without success
Question:
Is it possible to activate the button when the window (argument this in my code) is disabled?
Child window can't be independently enabled when parent window is disabled. You can instead enable all children, then go back and enable the particular button.
Note, if you have IDCANCEL button, and you disable it, then the dialog's close button is not functioning either and it gets confusing. You may want to avoid disabling the cancel button and override OnCancel
void CMyDialog::enable_children(bool enable)
{
auto wnd = GetWindow(GW_CHILD);
while (wnd)
{
wnd->EnableWindow(enable);
wnd = wnd->GetWindow(GW_HWNDNEXT);
}
}
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
enable_children(FALSE);
//re-enable one button
if(GetDlgItem(IDCANCEL)) GetDlgItem(IDCANCEL)->EnableWindow(TRUE);
return TRUE;
}
void OnCancel()
{
MessageBox(L"cancel...");
CDialog::OnCancel();
}

initialize double click on edit control in MFC

I am trying to set an mouse click event on editbox and when I am double clicking on edit box it should bring up a message box.
ON_WM_LBUTTONDBLCLK(IDC_EDITItem, &MessageManage::OnItemDoubleClick)
void MessageManage::OnItemDoubleClick()
{
MessageBox( m_strItemMsg, "Sample code", MB_OK | MB_ICONINFORMATION );
}
At alternative is to just use PreTranslateMessage on your dialog:
BOOL CMFCApplication1Dlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_LBUTTONDBLCLK &&
pMsg->hwnd == ::GetDlgItem(m_hWnd, IDC_EDIT1))
{
AfxMessageBox(_T("Run Code"));
return TRUE; //Important!!! Message is handled
}
return CDialogEx::PreTranslateMessage(pMsg);
}
It's not taking double click event from edit box
One way to accomplish this is to derive your own class from CEdit and handle ON_WM_LBUTTONDBLCLK(). The following code responded to the double click on an edit control in a sample program.
BEGIN_MESSAGE_MAP(MyEdit, CEdit)
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
// MyEdit message handlers
void MyEdit::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CEdit::OnLButtonDblClk(nFlags, point);
}

mfc dialog opens only after mouse move when using QT

I have an interesting issue. My MFC dialog CManageDlg is calling another MFC dialog CmyMfcDlg using this call, on press of a button
void CManageDlg::OnBnClickedBt()
{
CmyMfcDlg ipmfc;
if ( ipmfc.DoModal() != IDOK )
{
return MyError;
}
}
Here is :
BOOL CmyMfcDlg ::OnInitDialog()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog::OnInitDialog();
CString tmpStr;
UpdateData(FALSE);
CDC dc;
dc.Attach(::GetDC(this->m_hWnd));
int mx = dc.GetDeviceCaps(HORZRES);
int my = dc.GetDeviceCaps(VERTRES);
// lots of initializations
}
The problem is once OnBnClickedBt() is triggered by press of a button (ON_BN_CLICKED), CmyMfcDlg wait and does not open until mouse is moved! I do not know how these two are connected. I meant mouse move and opening the dialog.
EDIT1:
it turns out that this issue only happened when using QT User Interface, if I called the same function using UI written in MFC, it works fine with no problem!
Edit2:
I noticed also that this issue happened only when you open the dialog on the stack (modal) ipmfc.DoModal(), on the heap (modelless) everything is fine!

Changing Cursor of a button in MFC

I am trying to change cursor of a button in MFC dialog. i have used
BOOL CStartDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if ( m_changeCursor )
{
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
but it is changing cursor for whole dialog box. m_button is object of CButton class.
Please tell me how to change cursor of a button.I have tryed this also but not working
m_button1.SetCursor(::LoadCursor(NULL, IDC_HAND));
Call the LoadCursor() function and pass its returned value to the CMFCButton::SetMouseCursor() member function. Here is an example:
BOOL CExerciseDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_Calculate.SetMouseCursor(LoadCursor(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDC_CURSOR1)));
return TRUE;
}
refer http://www.functionx.com/visualc/controls/mfcbtn.htm#subtitle
also refer Api CWinApp::LoadCursor

CMFCCaptionMenuButton alternative?

I need to create a caption bar button for a CDockablePane which will call up a menu with various options. I tried to use CMFCCaptionMenuButton and the button and menu show up but the message map methods for the menu ids don't fire. The MFC documentation states that CMFCCaptionMenuButton is meant for internal infrastructure and not really for your code.
So assuming that is what my problem is should I be using a CMFCCaptionBarButton and then making a separate popup menu? Has anyone made a similar caption bar based menu in MFC before?
Here's some slimmed down code snippets in case I just made a stupid mistake in hooking up the events:
BEGIN_MESSAGE_MAP(CDockPane, CDockablePane)
ON_COMMAND(ID_MORPH_BROWSER, OnMorphBrowser)
END_MESSAGE_MAP()
void CDockPane::OnPressButtons(UINT nHit)
{
// only for custom button handling don't call base
// close, maximize, and pin will be handled by default
switch (nHit)
{
case ID_MORPHTEST:
{
CMorphMenuButton* pButton = dynamic_cast<CMorphMenuButton*>(m_arrButtons.GetAt(m_morphIndex));
pButton->ShowMenu(this);
break;
}
}
}
void CDockPane::SetCaptionButtons()
{
CDockablePane::SetCaptionButtons(); // for close, pin etc
m_morphIndex = m_arrButtons.Add(new CMorphMenuButton(ID_MORPHTEST));
}
void CDockPane::OnMorphBrowser()
{
// do stuff on menu item click
}
Edit: Removed previous code no longer in use
Now that the sound of crickets chirping has dwindled in the background I guess I'll post the workaround I currently have in place:
Instead of inheriting and extending CMFCCaptionMenuButton I build my class by extending CMFCCaptionButton. I then create a menu and provide a ShowMenu method to be explicitly called when handling the custom button events as well as overriding GetIconID to return a particular system icon for the button for each menu added to the caption bar ending up with something like this for the example outlined in the question:
#pragma once
// CMorphMenuButton command target
class CMorphMenuButton : public CMFCCaptionButton
{
public:
CMorphMenuButton(UINT nHit);
virtual ~CMorphMenuButton();
virtual CMenuImages::IMAGES_IDS GetIconID (BOOL bHorz, BOOL bMaximized) const;
void ShowMenu(CWnd* pWnd);
private:
CMenu m_dockMenu;
CMenu* m_subMenu;
};
// MorphMenuButton.cpp : implementation file
//
#include "stdafx.h"
#include "MorphMenuButton.h"
// CMorphMenuButton
CMorphMenuButton::CMorphMenuButton(UINT nHit)
: CMFCCaptionButton(nHit)
{
SetMiniFrameButton(); // already defaulted?
m_dockMenu.LoadMenu(IDR_DOCKPANE); // resource ID for dock pane menus
}
CMorphMenuButton::~CMorphMenuButton()
{
m_dockMenu.DestroyMenu();
}
CMenuImages::IMAGES_IDS CMorphMenuButton::GetIconID(BOOL bHorz, BOOL bMaximized) const
{
return CMenuImages::IdArrowForward;
}
void CMorphMenuButton::ShowMenu(CWnd* pWnd)
{
CRect windowRect, buttonRect;
pWnd->GetWindowRect(&windowRect);
buttonRect = GetRect();
CPoint menuPos(windowRect.left + buttonRect.right, windowRect.top + buttonRect.bottom);
m_subMenu = m_dockMenu.GetSubMenu(0);
if (!m_subMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, menuPos.x, menuPos.y, pWnd))
{
DWORD id = GetLastError();
wchar_t errMsg[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, id, 0, errMsg, sizeof(errMsg), 0);
MessageBox(0, errMsg, L"Error", MB_OK);
}
}
The setting of caption bar buttons and handling of click events for both buttons and menus are the same as defined in the question and this works.