I need to move window by right mouse button. The window has no caption, titlebar. By left button it works
void CMyHud::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
SendMessage(WM_SYSCOMMAND, SC_MOVE|0x0002);
CDialogEx::OnLButtonDown(nFlags, point);
}
But if I place this code on OnRButtonDown it dosen't work. What is the problem?
Well, the solution is found, thanks to Mark Ransom:
CRect pos;
void CMyHud::OnRButtonDown(UINT nFlags, CPoint point)
{
pos.left = point.x;
pos.top = point.y;
::SetCapture(m_hWnd);
CDialogEx::OnRButtonDown(nFlags, point);
}
void CMyHud::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* pWnd = CWnd::FromHandle(m_hWnd);
CRect r;
if(GetCapture() == pWnd)
{
POINT pt;
GetCursorPos(&pt);
GetWindowRect(r);
pt.x -= pos.left;
pt.y -= pos.top;
MoveWindow(pt.x, pt.y, r.Width(), r.Height(),TRUE);
}
CDialogEx::OnMouseMove(nFlags, point);
}
void CMyHud::OnRButtonUp(UINT nFlags, CPoint point)
{
ReleaseCapture();
CDialogEx::OnRButtonUp(nFlags, point);
}
In your OnRButtonDown function, do a SetCapture to ensure all mouse messages are routed to your window while the mouse button is down. Also store the mouse position in a member variable. Now, in your OnMouseMove function, check to see if GetCapture returns an object with the same HWND as yours - if it does, calculate the difference between the current mouse position and the saved one, then call MoveWindow to move the window.
In regards to left-mouse click:
SC_MOVE|0x0002 comes out as 0xF012 or SC_DRAGMOVE. This is apparently an undocumented constant. There is probably a good reason Microsoft doesn't want anybody to use this, that's why they have hidden it.
Also WM_SYSCOMMAND is a notification message. You should respond to it, not send it. To drag the window with left-mouse click:
message map ...
ON_WM_NCHITTEST()
LRESULT CMyDialog::OnNcHitTest(CPoint p)
{
//responding to a notification message
return HTCAPTION;
}
To drag the window with right-mouse you have to override OnRButtonDown, OnMouseMove, OnRButtonUp and make your own routine. But Window's behaviour gets very confusing. Is that really worth it?
you can use mouse message to realize.
WM_RBUTTONDOWN, WM_MOUSEMOVE
Related
I have an MFC dialog application where the user can upload a photo and it will be displayed within the dialog. After they have uploaded an image they will be able select a region of this image and for this I am using the CRectTracker class. My problem however is that the tracker is currently being displayed behind the image.
What I have tried so far is:
Googling how to set the z-order of the CRectTracker object, but cannot seem to find any documentation on it.
Set the z-order of the image to the bottom using SetWindowPos(&wndBottom,...) but the tracker still appears behind the image.
I am using a Picture Control (type Bitmap) to display the image.
EDIT:
I draw the tracker in the OnPaint() method of my dialog class like this:
CPaintDC dc(this);
m_tracker.Draw(&dc);
which is being called by this->Invalidate(); in OnLButtonDown and OnMouseMove whenever I need to refresh the tracker:
void CTAB3::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_tackerFlag)
{
int nHitTest;
nHitTest = m_tracker.HitTest(point);
if (nHitTest < 0) // if the mouse down point is outside of the rect tracker
{
m_start = point; // Record the start drawing point
m_bDraw = TRUE; // set m_bDraw (in handle funtion WM_MOUSEMOVE will test this to decide whether to draw)
}
else // if the mouse down point is inside of the rect tracker
{
m_tracker.Track(this, point, FALSE); // start drag the rect tracker
this->Invalidate(); // make the window paint to refresh the track
}
}
CDialogEx::OnLButtonDown(nFlags, point);
}
void CTAB3::OnMouseMove(UINT nFlags, CPoint point)
{
// m_bDraw is set to true in funtion OnLButtonDown and set to false in funtion OnLButtonDown
// m_bDraw is use for testing if the mouse is moved along with the left button is pressed.
if (m_bDraw)
{
m_tracker.m_rect.SetRect(m_start, point); // set the rect of rect tracker
this->Invalidate(); // make the window paint to refresh the rect
}
CDialogEx::OnMouseMove(nFlags, point);
}
EDIT2:
Based on #ConstantineGeorgiou comment I decided to keep trying to implement the tracker with the resize handles using Track() as this would be the preferred solution for my project. I finally managed to draw the tracker on top of the image, but I am not particularly happy with the solution I ended up with. Basically I moved the Draw() function to the mouse move handler, so that the tracker is drawn after the OnPaint() has drawn everything else on the dialog. If anyone has any suggestion on better ways to do this, I would be very grateful for any feedback.
This is how I implemented it with the Draw() in the OnMouseMove() handler:
//In the header file:
BOOL m_bDraw = FALSE
void CTAB3::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_tackerFlag)
{
m_tracker.Track(this, point, FALSE); // start drag the rect tracker
this->Invalidate();
m_bDraw = TRUE;
}
CDialogEx::OnLButtonDown(nFlags, point);
}
void CTAB3::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDraw)
{
//Draw tracker
CDC* dc = GetDC();
m_tracker.Draw(dc);
}
CDialogEx::OnMouseMove(nFlags, point);
}
BOOL CTAB3::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (m_tackerFlag)
{
if (pWnd == this && m_tracker.SetCursor(this, nHitTest))
{
return TRUE;
}
}
return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}
void CTAB3::OnPaint()
{
CPaintDC dc(this); // device context for painting
//m_tracker.Draw(&dc);
}
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.
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);
}
I've tried this in several ways. Deriving an object from CBCGPRibbonButton (same as CMFCRibbonButton) and using GetRect() from within the class, and having a click event find the button in the ribbon and get the rect.
What happens is that the rect is relative to the window that it is in. But if the panel is collapsed, then the window it is in is not the ribbon bar so it gets the wrong location.
I need a way of getting the location relative to the ribbon bar. Any ideas?
Ok, so I was trying to figure out what the rect was for the button:
When the panel had collapsed:
This is my solution:
class CMyButton : public CBCGPRibbonButton
{
DECLARE_DYNCREATE(CHeaderFooter)
public:
CMyButton()
{
};
CMyButton(
UINT nID,
LPCTSTR lpszText,
int nSmallImageIndex = -1,
int nLargeImageIndex = -1,
BOOL bAlwaysShowDescription = FALSE)
: CBCGPRibbonButton(nID, lpszText, nSmallImageIndex, nLargeImageIndex, bAlwaysShowDescription)
{
}
BOOL HasMenu() const override
{
return true;
}
CWnd* GetButtonWnd() const
{
CBCGPBaseRibbonElement* pDroppedDown = m_pParent->GetDroppedDown();
CWnd* pWnd;
if (pDroppedDown) // Was popup opened from a collapsed panel from the ribbon?
{
pWnd = pDroppedDown->GetPopupMenu()->GetMenuBar();
}
else
{
pWnd = m_pParent->GetParentRibbonBar();
}
return pWnd;
}
void OnShowPopupMenu() override
{
CRect rect = GetRect();
// pt is the bottom left corner of button relative to the window that
// it is contained in.
CPoint pt(rect.left, rect.bottom);
GetButtonWnd()->ClientToScreen(&pt); // convert pt to screen coordinates
... // do other stuff with that point
}
};
IMPLEMENT_DYNCREATE(CHeaderFooter, CBCGPRibbonButton)
which determines the CWnd that the button is part of so that the rect can be converted correctly to screen coordinates.
Calculate in screen coords.
Get the button rectangle from he ribbon. Use ClientToScreen and you have your screen coordinates now use your buttons parent handle with ScreenToClient and you have the cords relative to the ribbon bar.
PS: Even I don't know why to show a button when the ribbon is collapsed.
Is there anyway when mouse moving find the control mouse is over on it? I mean if you have a dialog with some labels and text boxes, and the mouse move to label, notify me that label name, after that if move it to text box notify the text box name.
If you handle WM_MOUSEMOVE within your dialog, you can grab the mouse position, convert it to dialog coordinates, and determine what control lies underneath the cursor point.
After some research, I came to this code which let me know if the mouse cursor is over my control in a dialog box.
//Handling mouse move in mfc dialog
void CDialogRoll::OnMouseMove(UINT nFlags, CPoint point)
{
CRect rect1;
m_FrameArea.GetClientRect(&rect1); //control rectangle
m_FrameArea.ClientToScreen(&rect1)
ScreenToClient(&rect1); //dialog coordinates`
if (point.x >= rect1.left && point.x <= rect1.right && point.y >= rect1.top &&
point.y <= rect1.bottom) {
char str[100];
sprintf(str, "%d-%d", point.x - rect1.left, point.y - rect1.top);
}
CDialogEx::OnMouseMove(nFlags, point);
}