I have a picture control box (a CStatic) in a dialog. When the user presses a button in the dialog I need the onPaint() to draw an image in it. The problem is, The image is drawn at the loading of the dialog. How do I prevent this and call it only at button press.
My onPaint code;
void CStaticGraph::OnPaint()
{
POINT xy[1000];
CPaintDC dc(this); // device context for painting
CRect Recto;
char LocDim[80];
GetWindowRect(&Recto);
CPoint pBottom,pTop,pLeft;
CPoint p[50];
pBottom.SetPoint(0,0);
pTop.SetPoint(0,Recto.Height());
pLeft.SetPoint(Recto.Width(),Recto.Height());
dc.MoveTo(pBottom);
dc.LineTo(pTop);
dc.LineTo(pLeft);
int y[] ={80,120,180,200};
int x=0;
for(int i=0; i<sizeof(y);i++){
p[i].SetPoint(Recto.Height()-x,y[i]);
if(i>0){
dc.MoveTo(p[i-1]);
dc.LineTo(p[i]);
}
x+=50;
}
}
As you can see I'm plotting a graph, I also need to pass data (the y[] values) at button press. I haven't done that yet.
Thanks.
Add a variable, such as a BOOL, to your CStaticGraph class to act as a flag to tell OnPaint() what to do. Initialize the variable in the constructor and change it when the button is clicked. For example:
In the header file for CStaticGraph add:
BOOL m_fButtonPressed;
In the CStaticGraph constructor add:
m_fButtonPressed = FALSE;
In your button click handler do something like:
void CStaticGraph::OnButtonClick()
{
m_fButtonPressed = TRUE;
Invalidate();
}
Then in your OnPaint only draw the graph when flag is set:
void CStaticGraph::OnPaint()
(
CPaintDC dc(this);
if ( m_fButtonPressed )
{
// Clear the window
return;
}
// Draw the graph
. . .
}
In addition to the Invalidate() in the other answer, you need to also change
for(int i=0; i<sizeof(y);i++)
to
for(int i=0; i<sizeof(y)/sizeof(int);i++)
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.
Im building a MFC c++ application in which i let the user read an image, draw lines on it and then save it.
so i have a "CImage" object which is called "Image" in which the user loads the image to.
and i have a device context object and i was able to draw lines on it
the device context object that is in run-time using "OnLButtonDown" and "OnLButtonUp" event handlers.
i then let the user save the image using "CImage.save" .. the image is saved but the device context drawn lines aren't there which is to be expected ..
but I DO want them to appear in the saved image..
the question is how can i get the device context Object to affect my CImage Object?
this is the event handler for mouse button down
void CProFilterDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
curser =point;
if (draw && Boundry.PtInRect(point) )
{
CDialogEx::OnLButtonDown(nFlags, point);
}
}
and this one when the mouse button is up
void CProFilterDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if (draw && Boundry.PtInRect(curser) && Boundry.PtInRect(point))
{
CClientDC dc(this);
dc.MoveTo(curser);
dc.LineTo(point);
CDialogEx::OnLButtonUp(nFlags, point);
}
}
this is where i load my Cimage object
void CProFilterDlg::OnBnClickedBtnBrowse()
{
CFileDialog Browse(true);
if(Browse.DoModal() == IDOK)
{
ImagePath = Browse.GetPathName();
}
image.Load(ImagePath);
}
and this is where i save my CImage
void CProFilterDlg::OnBnClickedSave()
{
CFileDialog Save(true);
if(Save.DoModal() == IDOK)
{
ImagePath = Save.GetPathName();
}
image.Save(ImagePath,Gdiplus::ImageFormatBMP);
}
From what you've shown, it appears you are using the wrong DC. You seem to be using the DC for the dialog (ie. CCLientDC) and not the actual CImage. You should be constructing the DC from
CImage::GetDC ().
That DC will have the currently selected bitmap.
Are you looking for CImage:BitBlt? It is used to copy a bitmap from the source device context to current device context.
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.
I've created a label and used setPixmap to attach a .png image to the label. I've also setWindowFlags to disable the title bar and create a frameless window. Because I've disabled those, it also disables the ability to drag anything around, so I want to create mouseevents (unless there's a better method) to position my label anywhere on the screen exactly like dragging the frame of a window. How would I do that? An example and brief explanation would be greatly appreciated.
reimplement the QMouseEvents you need .... like
void MyLabel::mousePressEvent(QMouseEvent* e)
{
m_moveDatWidget = true;
// so when the mousebutton got pressed, you set something to
// tell your code to move the widget ... consider maybe you
// want to move it only on right button pressed ...
}
void MyLabel::mouseReleaseEvent(QMouseEvent* e)
{
m_moveDatWidget = false;
// after releasing do not forget to reset the movement variable
}
void MyLabel::mouseMoveEvent(QMouseEvent* e)
{
// when in 'moving state' ...
if (m_moveDatWidget)
{
// move your widget around using QWidget::move(qreal,qreal)
}
}
this is only a really basic implementation but it should do well if you calculate the desired movement correct :)
I would implement the label dragging by mouse in the following way:
class Label : public QLabel
{
public:
// Implement the constructor(s)
protected:
void Label::mouseMoveEvent(QMouseEvent* event)
{
if (!m_offset.isNull()) {
move(event->globalPos() - m_offset);
}
QLabel::mouseMoveEvent(event);
}
void Label::mousePressEvent(QMouseEvent* event)
{
// Get the mouse offset in the label's coordinates system.
m_offset = event->globalPos() - pos();
QLabel::mousePressEvent(event);
}
void Notifier::mouseReleaseEvent(QMouseEvent* event)
{
m_offset = QPoint();
QLabel::mouseReleaseEvent(event);
}
private:
// The mouse pointer offset from the top left corner of the label.
QPoint m_offset;
};