CRichEditCtrl::SetRedraw() in OnMouseMove handler causes infinite loop - mfc

Oversimplifying, I have the next WM_MOUSEMOVE message handler in my CRichEditCtrlEx class, which is derived from the CRichEditCtrl:
void CRichEditCtrlEx::OnMouseMove(UINT nFlags, CPoint Point)
{
SetRedraw(FALSE);
// some actions, which should not cause rich edit redrawing
SetRedraw(TRUE);
}
The problem is that SetRedraw(TRUE), preceded by the SetRedraw(FALSE), somehow places a new WM_MOUSEMOVE message in the message queue, so OnMouseMove handler will be called infinitely, even if the mouse doesn't move.
Trying to locate the trouble, I've experimented with the following simple handlers:
void CRichEditCtrlEx::OnMouseMove(UINT nFlags, CPoint Point)
{
RedrawWindow();
}
or
void CRichEditCtrlEx::OnMouseMove(UINT nFlags, CPoint Point)
{
Invalidate();
},
but they don't cause infinite loops.
I've also tried to validate the client area, but it didn't help:
void CRichEditCtrlEx::OnMouseMove(UINT nFlags, CPoint Point)
{
SetRedraw(FALSE);
// some actions, which should not cause rich edit redrawing
CRect rc;
GetClientRect(&rc);
ValidateRect(&rc);
SetRedraw(TRUE);
}
What is wrong with the SetRedraw()?
Any idea would be appreciated.
UPD: I see that WM_MOUSEMOVE message may be caused not only by a mouse movement, but sometimes also by a window drawing. An infinite loop can be avoided by storing the last mouse position and checking whether the mouse was really moved, but it seems for me like a workaround.

Related

Can't detect mouse down on MFC Picture control

I am trying to add a picture control in a mfc dialog. I want to get the x,y coordinates of the pixel when I click on the picture control. I am using this code as a reference code and following this discussion to detect the click on the picture control.
A separate class has been derived for the picture control and PreTranslateMessage has been implemented.
BOOL CPictureCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if (pMsg->message == WM_LBUTTONDOWN && GetDlgItem(IDC_STATIC_BITMAP)->GetSafeHwnd() == pMsg->hwnd)
{
CPoint point(pMsg->pt);
ScreenToClient(&point);
OnLButtonDown(pMsg->wParam, point); //passes the coordinates of the clicked Point in the dialog box
}
return CStatic::PreTranslateMessage(pMsg);
}
In the main Dialog class I am calling the OnLButtonDown as follows
void CCPictureCtrlDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rect;
m_picCtrl.GetWindowRect(&rect);
ScreenToClient(&rect);
if (rect.PtInRect(point))
{
// Do something
}
CDialog::OnLButtonDown(nFlags, point);
}
PreTranslateMessage is only triggered when i hover the mouse over the picture control, it does not trigger when i click on the picture. and the OnLButtonDown of the main dialog is does not trigger when
I click on the picture control, it only triggers when I click anywhere other than the picture control.
Thanks.

MFC click and move/drag dialog window

I am currently working on finishing some code handed off to me. It was written in MFC in Visual Studio 2005 years ago, was put on hold, and now is brought to me.
While I know C++, I have spent the last ~2 months studying the code and learning MFC and it's starting to come together.
The GUI for the code is an SWF flash file embedded in an invisible dialog window. I do not have the source code for the SWF file so will probably, in the future, redo it in WPF or something. I have the WMMode set to "Window" because in Transparent/Opaque mode it doesn't display properly, where it flashes/blinks everytime a mouse event is captured.
Anyhow, in Win XP/Vista, clicking and dragging the flash control works. In windows 7/8.1, it won't move.
Happy to provide any and all info needed. I'm still a little overwhelmed by MFC dialogs so unsure what you'd all like to see.
I found this question: Moving window by click-drag on a control
Which looks like it solves a lot of the issue. However, I don't want the whole control to be clickable like this, only the top part. Unfortunately, in the MS Resource view, the ActiveX control is blank as the SWF is only loaded at runtime; I've tried to find resources for this kind of thing but it's very difficult as I am unsure of the technical terms to use.
EDIT
I have attempted this by creating a very simple MFC app that has a Static Text control and nothing else. I am trying to get it to work by clicking on the static text (though I may be painting myself into a corner as it does not have a built-in lButtonDown event).
Here is the relevant code:
class MyDialog : public CDialog
{
public:
MyDialog(CWnd* pParent = NULL) : CDialog(MyDialog::IDD, pParent)
{ }
// Dialog Data, name of dialog form
enum{ IDD = INTERFACE1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
//Called right after constructor. Initialize things here.
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
pText = (CStatic *)GetDlgItem(ID_TEXT);
pText->SetWindowTextW(_T("Hello World!"));
return true;
}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
private:
CStatic * pText;
public:
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Overridden Method:
afx_msg void MyDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnNcLButtonDown(HTCAPTION, point);
}
I have also tried setting nFlags to 0x2, calling OnLButtonDown (as opposed to onNcLButtonDown), various other things. The message fires but the window does not move (it does move from the title bar, as normal). What am I missing?
Actually lets try this code instead with ON_WM_NCHITTEST(). This will drag the dialog if you click the mouse anywhere in client area (client area acts as caption). There is a line rc.bottom = rc.top + 100 if you uncomment it then it will only drag if you click the top section (I picked the number 100 at random).
//declare:
afx_msg LRESULT OnNcHitTest(CPoint point);
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_NCHITTEST()
END_MESSAGE_MAP()
LRESULT MyDialog::OnNcHitTest(CPoint point)
{
ScreenToClient(&point);
CRect rc;
GetClientRect(&rc);
//rc.bottom = rc.top + 100;
if (rc.PtInRect(point))
return HTCAPTION;
return CDialog::OnNcHitTest(point);
}
Second option:
If we want to move the dialog by clicking on a child control, and if that control captures the mouse, then try this method instead. ***Note, test to make sure the control works properly after it is moved.
BOOL MyDialog::PreTranslateMessage(MSG *msg)
{
if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON))
{
CPoint p;
GetCursorPos(&p);
CRect r;
ActiveX->GetWindowRect(&r);
if (r.PtInRect(p))
{
ReleaseCapture();
SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessage(WM_NCLBUTTONUP, HTCAPTION, 0);
return 1;
}
}
return CDialogEx::PreTranslateMessage(msg);
}

Drag CMDIChildWnd using client area

I'm trying to create a CMDIChildWnd without title bar draggable using the mouse on the client area.
into the message map I've added
ON_WM_LBUTTONDOWN()
and
void CChildFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
SendMessage(WM_SYSCOMMAND, SC_MOVE | 0x0002);
}
the result is a child windows that moves using the mouse as required but only inside its area.
Any idea on how I can proceed?
I did it using a little trick. The class to implement the behaviour is the derived class of the CFormView and I'm able to move the window as expected sending the htcaption message to the parent
void derived_CFormView::OnLButtonDown(UINT nFlags, CPoint point)
{
CFormView::OnLButtonDown(nFlags, point);
GetParentFrame()->PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}
void derived_CFormView::OnLButtonUp(UINT nFlags, CPoint point)
{
CFormView::OnLButtonUp(nFlags, point);
GetParentFrame()->PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}

How to paint an MFC element?

I'm not very good at MFC.
All i want to do is paint element (button, for example) when the button was pressed. I found this, but it only works when rendering dialog.
HBRUSH CSmartDeviceDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (nCtlColor==CTLCOLOR_STATIC)
{
return a;
}
if (nCtlColor==CTLCOLOR_EDIT){
return a;
}
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);;
}
Give me some advice how do I change it to smth like this:
void CSmartDeviceDlg::OnClick()
{
//some code to paint my elements
}
Thx for future responses and do not judge strictly =)
You should obviously place the button on the dialog somewhere physically. Code wise you should override the button onDraw to draw a custom button.
What you will achieve depends on what you want to do with the button, you can override as low as the actual drawing rectangle which represents the button and draw over it whatever you like.
You would need to invalidate the rectangle occupied by your elements to force windows to repaint them, like
CRect rcElement;
GetDlgItem(IDC_ELEMENT)->GetWindowRect(&rcElement); // in screen coordinates
ScreenToClient(&rcElement); // convert to client coordinates
InvalidateRect(&rcElement);
Note that this can cause "flickering" of the elements because the background will be erased before the elements are repainted.

How to capture MouseMove event in a MFC Dialog Based application for a checkbox?

My application is a VC6 MFC dialog based application with multiple property pages.
I have to capture a mousemove event over a control, for example Checkbox.
How can I capture the mousemove events over a checkbox in MFC?
A checkbox is a button control (eg. CWnd). Derive your own class from CCheckBox and handle the OnMouseMove event.
Per request...assuming a class derived from CButton...
BEGIN_MESSAGE_MAP(CMyCheckBox, CButton)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CMyCheckBox::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnMouseMove(nFlags, point);
}
Thanks for your replies.. I found a way to get the mousemove event for my app.
WM_SETCURSOR windows message gets the mouse move. It returns the Cwnd pointer for a control and the dialog.
Find my code below.
BOOL CMyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
CWnd* pWndtooltip = GetDlgItem(IDC_STATIC_TOOLTIP);
if (pWnd != this)
{
if (IDC_SN_START_ON == pWnd->GetDlgCtrlID())
pWndtooltip->ShowWindow(SW_SHOW);
}
else
pWndtooltip->ShowWindow(SW_HIDE);
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
return true;
}
I found in #raj's OnSetCursor() code, that the associated Member variable for IDC_STATIC_TOOLTIP is that variable to which you assign the desired tool tip text. For example, if the associated variable is m_strToolTip, assign the desired text to display during the hovering event as follows:
m_strToolTip.Format("%s", "Tool tip text goes here");
I also found that UpdateData() was required upon entry into the event handler and UpdateData(FALSE) was required before the return. The SetCursor() call seems to have no effect when commented.
You can also override CDialog::PreTranslateMessage:
BOOL CSomeDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_checkBox->m_hWnd)
{
...
}
return CDialog::PreTranslateMessage(pMsg);
}