I want to create an MFC application that move an rectangle in the client area by hold left mouse and move to new position. But the rectangle move too fast although I move the mouse slowly.
My code here:
class CEmpWnd : public CFrameWnd
{
protected:
POINT lpStart, rpStart, oPoint;
bool clicked;
public:
CEmpWnd(void);
virtual ~CEmpWnd(void);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
int CEmpWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpStart.x = 0;
lpStart.y = 0;
rpStart.x = 100;
rpStart.y = 100;
clicked = false;
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
SetTimer(ID_TIMER, 250, NULL);
SetWindowText(L"Phùng Khánh Hiên ");
return 0;
}
void CEmpWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
Rectangle(dc, lpStart.x, lpStart.y, rpStart.x, rpStart.y); //Draw a rectangle
}
void CEmpWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
if(point.x >= lpStart.x && point.x <= rpStart.x && point.y >= lpStart.y && point.y <= rpStart.y) //If click on rectangle area
{
clicked = true;
oPoint = point;
}
}
void CEmpWnd::OnMouseMove(UINT nFlags, CPoint point)
{
if(clicked == true)
{
lpStart.x += (point.x - oPoint.x); //Increase left-top and right-bot Points
rpStart.x += (point.x - oPoint.x);
lpStart.y += (point.y - oPoint.y);
rpStart.y += (point.y - oPoint.y);
lpStart.x = max(0, lpStart.x); //Keep current size of rectangle.
lpStart.y = max(0, lpStart.y);
rpStart.x = max(100, rpStart.x);
rpStart.y = max(100, rpStart.y);
Invalidate(); //Eraze old Rectangle and redraw new one.
}
}
void CEmpWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
clicked = false; //Release drawing.
}
I am new in MFC. Can someone help me please ? Thank everyone.
The Problem is that you always add the total Change again to the current Position.
(point.x - oPoint.x) is the complete horizontal change.
But lpStart contains always the current rectangle coords.
Save the rectangle coords of lpStart also as a oRect when the button is clieked.
CRect newRect = oRect;
newRect.OffsetRect(point.x - oPoint.x, point.y - oPoint.y);
lpStart = newRect;
Invalidate();
Or set oPoint to Point after you moved the rectangle. with tis trick you always track only the relative changes.
...your code
// Save the new oPoint to makle the movements relative
oPoint = Point;
Invalidate();
Related
I've got an application which handles zooming in/out using the mouse wheel with this event in Qt Creator.
cpp
void QNodeView::wheelEvent(QWheelEvent* event) {
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
// Scale the view / do the zoom
double scaleFactor = 1.15;
if(event->delta() > 0) {
// Zoom in
scale(scaleFactor, scaleFactor);
} else {
// Zooming out
scale(1.0 / scaleFactor, 1.0 / scaleFactor);
}
}
This is in the header file
h
protected:
//Take over the interaction
virtual void wheelEvent(QWheelEvent* event);
How can I add the ability to pan with the middle mouse button being pressed the user dragging the cursor?
I can post the project code if necessary just ask.
Thanks
Project files link (Qt Creator project)
https://www.dropbox.com/s/gbt4qqtdedltxek/QNodesEditor-master_01.zip?dl=0
At first, introduce some new member variables into your viewer class:
class QNodeView : public QGraphicsView
{
// ...
private:
int m_originalX = 0;
int m_originalY = 0;
bool m_moving = false;
};
Then reimplement mousePressEvent(), mouseMoveEvent(), and mouseReleaseEvent().
void QNodeView::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::MiddleButton)
{
// store original position
m_originalX = event->x();
m_originalY = event->y();
// set the "moving" state
m_moving = true;
}
}
void QNodeView::mouseMoveEvent(QMouseEvent* event)
{
if (m_moving)
{
// panning operates in the scene coordinates using x,y
QPointF oldp = mapToScene(m_originalX, m_originalY);
QPointF newp = mapToScene(event->pos());
QPointF translation = newp - oldp;
translate(translation.x(), translation.y());
m_originalX = event->x();
m_originalY = event->y();
}
}
void QNodeView::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::MiddleButton)
{
m_moving = false;
}
}
following my another question suppose that I have two public member data in my class used as modes:
bool WantToSetRectangle;
bool WantToDrawRectangle;
and also I have two public member data which are vectors with 4 members that are used to set a rectangle and draw a rectangle.
vector<int>ViewRectangle;
vector<int>RectangleToDraw;
and this is the implementation of the class's OnDraw function runned after each navigation task like zooming,paning and etc.
void COpenGLControl::OnDraw(CDC *pDC)
{
// TODO: Camera controls
wglMakeCurrent(hdc,hrc);
glLoadIdentity();
gluLookAt(0,0,1,0,0,0,0,1,0);
glTranslatef(m_fPosX, m_fPosY, 0.0f);
glScalef(m_fZoom,m_fZoom,1.0);
if (WantToSetRectangle)
setViewRectangle();
if (WantToDrawRectangle)
DrawRectangleOnTopOfTexture();
wglMakeCurrent(NULL, NULL);
}
Now I create two instances of the class COpenGLControl in my CDialogEx:
COpenGLControl m_oglWindow1;
COpenGLControl m_oglWindow2;
as you see in the first picture in my another question the m_oglWindow1 is the bigger window and m_oglWindow2 is the smaller one. I set the modes of two windows as follows:(these modes are set false in the constructor)
m_oglWindow1.WantToSetRectangle = true;
m_oglWindow2.WantToDrawRectangle = true;
each time the onDraw function of the m_oglWindow1 is called the ViewRectangle is set. these data should be dynamically passed to the RectangleToDraw of m_oglWindow2 and immediately after that the OnDraw function of m-oglWindow2 should be called to draw the extent rectangle on the smaller window that is always in the Full Extent mode.
Remember that for tasks like Fixed Zoom in, I can easily write this in the button click handlers of my CDialogEx:
void CMyOpenGLTestDlg::OnBnClickedButton4()
{
// TODO: Add your control notification handler code here
m_oglWindow1.FixedZoomOut();
m_oglWindow2.RectangleToDraw = m_oglWindow1.ViewRectangle;
m_oglWindow2.OnDraw(NULL);
}
but in other tasks like pan,zoom in to the point and zoom out of the point that are implemented using mouse-event handlers of the class COpenGLControl, I need some kind of rea-time data exchange between two instances of the class:
void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (WantToPan)
{
if (m_fLastX < 0.0f && m_fLastY < 0.0f)
{
m_fLastX = (float)point.x;
m_fLastY = (float)point.y;
}
int diffX = (int)(point.x - m_fLastX);
int diffY = (int)(point.y - m_fLastY);
m_fLastX = (float)point.x;
m_fLastY = (float)point.y;
if (nFlags & MK_MBUTTON)
{
m_fPosX += (float)0.2f*m_fZoomInverse*diffX;
m_fPosY -= (float)0.2f*m_fZoomInverse*diffY;
}
OnDraw(NULL);
}
CWnd::OnMouseMove(nFlags, point);
}
void COpenGLControl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (WantToUseZoomTool)
{
if (nFlags & MK_LBUTTON)
{
m_fZoom = 1.05f*m_fZoom;
m_fZoomInverse = 1/m_fZoom;
int diffX = (int)(point.x - oglWindowWidth/2);
int diffY = (int)(point.y - oglWindowHeight/2);
m_fPosX -= (float)diffX;
m_fPosY += (float)diffY;
}
OnDraw(NULL);
}
CWnd::OnLButtonDown(nFlags, point);
}
void COpenGLControl::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (WantToUseZoomTool)
{
if (nFlags & MK_RBUTTON)
{
m_fZoom = 0.95f*m_fZoom;
m_fZoomInverse = 1/m_fZoom;
int diffX = (int)(point.x - oglWindowWidth/2);
int diffY = (int)(point.y - oglWindowHeight/2);
m_fPosX -= (float)diffX;
m_fPosY += (float)diffY;
}
OnDraw(NULL);
}
CWnd::OnRButtonDown(nFlags, point);
}
Each control can notify the parent dialog that an update of the other control is needed.
GetParent()->PostMessage(UWM_UPDATE2, 0, 0);
where the user-defined messages are:
#define UWM_UPDATE1 (WM_APP + 1)
#define UWM_UPDATE2 (WM_APP + 2)
The dialog can handle these messages if you put ON_MESSAGE into its message map.
ON_MESSAGE(UWM_UPDATE2, OnUpdate2)
LRESULT CMyOpenGLTestDlg::OnUpdate2(WPARAM, LPARAM)
{
}
I am not able to access the private member variables from member function CAboutDlg::OnLButtonDown.
When I attempted to assign a new value to ellipseColor i get the undeclared identifier error. When I try to access it through its class CCSIT861a3VasilkovskiyDlg::ellipseColorthe compiler says that it is inaccessible.
What have I missed, and how can I access/modify these variable?
Header File:
#pragma once
// CCSIT861a3VasilkovskiyDlg dialog
class CCSIT861a3VasilkovskiyDlg : public CDialogEx
{
// Construction
public:
CCSIT861a3VasilkovskiyDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
enum { IDD = IDD_CSIT861A3VASILKOVSKIY_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
private:
COLORREF ellipseColor;
COLORREF centerRectColor;
COLORREF rightRectColor;
CRect centerRect;
CRect rightRect;
CSize ellipse;
bool mouseCaptured;
};
Implementation File:
#include "stdafx.h"
#include "CSIT861a3 Vasilkovskiy.h"
#include "CSIT861a3 VasilkovskiyDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
// CCSIT861a3VasilkovskiyDlg dialog
CCSIT861a3VasilkovskiyDlg::CCSIT861a3VasilkovskiyDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CCSIT861a3VasilkovskiyDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
ellipseColor = RGB(255, 0, 0);
centerRectColor = RGB(0, 0, 0);
rightRectColor = RGB(255, 0, 0);
centerRect.left = 40;
centerRect.top = 20;
centerRect.right = 55;
centerRect.bottom = 80;
rightRect.left = 75;
rightRect.top = 35;
rightRect.right = 90;
rightRect.bottom = 50;
ellipse.cx = 10;
ellipse.cy = 10;
mouseCaptured = false;
}
void CCSIT861a3VasilkovskiyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CCSIT861a3VasilkovskiyDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()
// CCSIT861a3VasilkovskiyDlg message handlers
BOOL CCSIT861a3VasilkovskiyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 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
return TRUE; // return TRUE unless you set the focus to a control
}
void CCSIT861a3VasilkovskiyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CCSIT861a3VasilkovskiyDlg::OnPaint()
{
//Create pen and pointer to the old pen
CPen blackPen;
blackPen.CreatePen(PS_SOLID, 1, RGB (0, 0, 0));
//Create a bursh
CBrush blackBrush(centerRectColor);
CBrush redBrush(rightRectColor);
CBrush hatchRedBrush(HS_CROSS, ellipseColor);
//Set up object for painting
CPaintDC dc (this);
CRect rect;
GetClientRect (&rect);
dc.SetMapMode (MM_ANISOTROPIC);
dc.SetWindowExt (100, 100);
dc.SetViewportExt (rect.Width (), rect.Height ());
//select pen
dc.SelectObject(&blackPen);
//Rectangle red interior color
dc.SelectObject(&redBrush);
dc.Rectangle(rightRect); //(75, 35, 90, 50);
//Rectangel black interior
dc.SelectObject(&blackBrush);//Select Brush
dc.Rectangle( centerRect);//(40, 20, 55, 80);
//Ellipse
dc.SelectObject(&hatchRedBrush);//Select Brush
dc.Ellipse(ellipse.cx, ellipse.cy, (ellipse.cx) + 10, (ellipse.cy) + 10);//10, 10, 20, 20);
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CCSIT861a3VasilkovskiyDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CAboutDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
//SetCapture();
CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowExt(100, 100);
dc.SetViewportExt(rect.Width(), rect.Height());
CRect rectEllipse(10, 10, 20, 20);
CDialogEx::OnLButtonDown(nFlags, point);
}
void CAboutDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
ReleaseCapture ();
CDialogEx::OnLButtonUp(nFlags, point);
}
void CAboutDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CDialogEx::OnMouseMove(nFlags, point);
}
Your OnLButtonDown() is a member function of CAboutDlg, but you are trying to access a private, non-static data member of CCSIT861a3VasilkovskiyDlg, which is a different class.
You probably meant to have functions OnLButtonDown(), OnLButtonUp(), and OnMouseMove() as member functions of CCSIT861a3VasilkovskiyDlg, and not as member functions of CAboutDlg.
I painted three objects onto a window using CPaintDC, and now I am trying to get one of them (the ellipse) to become draggable by using OnMouseMove event handler. I am just playing around with MFC and am lost as to how to get this to work.
This is what I have tried in OnMouseMove:
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowExt(100, 100);
CRect rectEllipse(ellipse.cx, ellipse.cy,(ellipse.cx) + 10, (ellipse.cy) + 10);//(10, 10, 20, 20);dc.Ellipse(ellipse.cx, ellipse.cy, (ellipse.cx) + 10, (ellipse.cy) + 10);//10, 10, 20, 20);
//CPoint ellipseDest;
//dc.SetViewportExt(rect.Width(), rect.Height());
if (mouseCaptured)
{
ellipse.cx = point.x;
ellipse.cy = point.y;
InvalidateRect(rectEllipse, TRUE);
}
CDialogEx::OnMouseMove(nFlags, point);
Please find my code pasted below.
Thanks so much for the help.
#include "stdafx.h"
#include "CSIT861a3 Vasilkovskiy.h"
#include "CSIT861a3 VasilkovskiyDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
public:
// afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
// afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
// afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
// ON_WM_LBUTTONDOWN()
// ON_WM_LBUTTONUP()
// ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
// CCSIT861a3VasilkovskiyDlg dialog
CCSIT861a3VasilkovskiyDlg::CCSIT861a3VasilkovskiyDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CCSIT861a3VasilkovskiyDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
ellipseColor = RGB(255, 0, 0);
centerRectColor = RGB(0, 0, 0);
rightRectColor = RGB(255, 0, 0);
centerRect.left = 40;
centerRect.top = 20;
centerRect.right = 55;
centerRect.bottom = 80;
rightRect.left = 75;
rightRect.top = 35;
rightRect.right = 90;
rightRect.bottom = 50;
ellipse.cx = 10;
ellipse.cy = 10;
mouseCaptured = false;
}
void CCSIT861a3VasilkovskiyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CCSIT861a3VasilkovskiyDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
// CCSIT861a3VasilkovskiyDlg message handlers
BOOL CCSIT861a3VasilkovskiyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 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
return TRUE; // return TRUE unless you set the focus to a control
}
void CCSIT861a3VasilkovskiyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CCSIT861a3VasilkovskiyDlg::OnPaint()
{
//Create pen and pointer to the old pen
CPen blackPen;
blackPen.CreatePen(PS_SOLID, 1, RGB (0, 0, 0));
//Create a bursh
CBrush blackBrush(centerRectColor);
CBrush redBrush(rightRectColor);
CBrush hatchRedBrush(HS_CROSS, ellipseColor);
//Set up object for painting
CPaintDC dc (this);
CRect rect;
GetClientRect (&rect);
dc.SetMapMode (MM_ANISOTROPIC);
dc.SetWindowExt (100, 100);
dc.SetViewportExt (rect.Width (), rect.Height ());
//select pen
dc.SelectObject(&blackPen);
//Rectangle red interior color
dc.SelectObject(&redBrush);
dc.Rectangle(rightRect); //(75, 35, 90, 50);
//Rectangel black interior
dc.SelectObject(&blackBrush);//Select Brush
dc.Rectangle( centerRect);//(40, 20, 55, 80);
//Ellipse
dc.SelectObject(&hatchRedBrush);//Select Brush
dc.Ellipse(ellipse.cx, ellipse.cy, (ellipse.cx) + 10, (ellipse.cy) + 10);//10, 10, 20, 20);
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CCSIT861a3VasilkovskiyDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//void CAboutDlg::OnLButtonDown(UINT nFlags, CPoint point)
//{
// // TODO: Add your message handler code here and/or call default
// //SetCapture();
// CClientDC dc(this);
// CRect rect;
// GetClientRect(&rect);
// dc.SetMapMode(MM_ANISOTROPIC);
// dc.SetWindowExt(100, 100);
// dc.SetViewportExt(rect.Width(), rect.Height());
// CRect rectEllipse(10, 10, 20, 20);
//
// ellipseColor = RGB(0,0,0);
//
// CDialogEx::OnLButtonDown(nFlags, point);
//}
//void CAboutDlg::OnLButtonUp(UINT nFlags, CPoint point)
//{
// // TODO: Add your message handler code here and/or call default
// ReleaseCapture ();
// CDialogEx::OnLButtonUp(nFlags, point);
//}
//void CAboutDlg::OnMouseMove(UINT nFlags, CPoint point)
//{
// // TODO: Add your message handler code here and/or call default
//
// CDialogEx::OnMouseMove(nFlags, point);
//}
void CCSIT861a3VasilkovskiyDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowExt(100, 100);
dc.SetViewportExt(rect.Width(), rect.Height());
CRect rectEllipse(ellipse.cx, ellipse.cy, 20, 20);//(10, 10, 20, 20);
CRgn circle;
dc.LPtoDP(rectEllipse);
circle.CreateEllipticRgnIndirect(rectEllipse);
if (circle.PtInRegion(point))
{
SetCapture();
mouseCaptured = true;
ellipseColor = RGB(0,0,255);
InvalidateRect(rectEllipse, FALSE);
}
CDialogEx::OnLButtonDown(nFlags, point);
}
void CCSIT861a3VasilkovskiyDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
ReleaseCapture ();
mouseCaptured = false;
CDialogEx::OnLButtonUp(nFlags, point);
}
void CCSIT861a3VasilkovskiyDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (mouseCaptured)
{
/*
Code to drag and drop and redraw ellipse
*/
InvalidateRect(rectEllipse, TRUE);
}
CDialogEx::OnMouseMove(nFlags, point);
}
I think Mark Ransom's comment is right, you need to invalidate two rectangles on MouseMove.
CRect rectEllipse(ellipse.cx, ellipse.cy,(ellipse.cx) + 10, (ellipse.cy) + 10);
if (mouseCaptured)
{ InvalidateRect(rectEllipse, TRUE);
ellipse.cx = point.x;
ellipse.cy = point.y;
CRect rectNew(ellipse.cx, ellipse.cy,(ellipse.cx) + 10, (ellipse.cy) + 10);
InvalidateRect(rectNew, TRUE);
}
Also, on OnLButtonUp()
if (mouseCaptured)
{ ellipseColor = RGB(255, 0, 0);
CRect rectEllipse(ellipse.cx, ellipse.cy,(ellipse.cx) + 10, (ellipse.cy) + 10);
InvalidateRect(rectEllipse, TRUE);
}
I'm using a CListCtrl/CListView report view (LVS_REPORT) in virtual mode (LVS_OWNERDATA) with LVS_EX_DOUBLEBUFFER enabled and I encounter ugly flickering. Double buffer have a real effect but it doesn't stop all flickering (without it very slow).
I'm not looking for switching to other controls that would require a high amount of rework (like ObjectListView)
How does the flickering behaves:
on column resize - the background is first clean using lightgray and after this is displayed the text (background is white)
on mouse scroll (animated) - for a very short time there is lightgray-bar displayed in the area where new lines are to be displayed.
It looks like it does clean the background using the default window background color (lightgray) for the area where it has to redraw.
How do I solve the flickering problem?
Try to do the following:
- Set Clip Children and Clip Sibling for paremt dialog of List Control.
- Make dirived from CListCtrl class. In this class overwrite OnEraseBkgnd. In the OnEraseBkgnd fill with background color area around of visible items of the list.
The OnEraseBkgnd can look like:
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
{
CBrush br;
CRect rcCli;
CRect rcItemsRect(0, 0, 0, 0);
int nHeadHeight = 0;
int nItems = GetItemCount();
GetClientRect(&rcCli);
CHeaderCtrl* pHeadCtrl = GetHeaderCtrl();
if (pHeadCtrl)
{
CRect rcHead;
pHeadCtrl->GetWindowRect(&rcHead);
nHeadHeight = rcHead.Height();
}
rcCli.top += nHeadHeight;
if (nItems > 0)
{
CPoint ptItem;
CRect rcItem;
GetItemRect(nItems - 1, &rcItem, LVIR_BOUNDS);
GetItemPosition(nItems - 1, &ptItem);
rcItemsRect.top = rcCli.top;
rcItemsRect.left = ptItem.x;
rcItemsRect.right = rcItem.right;
rcItemsRect.bottom = rcItem.bottom;
if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
rcItemsRect.left -= GetSystemMetrics(SM_CXEDGE) + 16;
}
br.CreateSolidBrush(GetBkColor());
if (rcItemsRect.IsRectEmpty())
pDC->FillRect(rcCli, &br);
else
{
if (rcItemsRect.left > rcCli.left) // fill left rectangle
pDC->FillRect(
CRect(0, rcCli.top, rcItemsRect.left, rcCli.bottom), &br);
if (rcItemsRect.bottom < rcCli.bottom) // fill bottom rectangle
pDC->FillRect(
CRect(0, rcItemsRect.bottom, rcCli.right, rcCli.bottom), &br);
if (rcItemsRect.right < rcCli.right) // fill right rectangle
pDC->FillRect(
CRect(rcItemsRect.right, rcCli.top, rcCli.right, rcCli.bottom), &br);
}
return TRUE;
}
I know only way to have flicker free is using double buffering or MemDC.
have found this article: Flicker-free-drawing-of-any-control
This article explains it well how to quickly perform Non Flickering drawing on your CListCtrl.
And it works excellent.
PS: VS 2005 doesn't have CMemDC class you will need to implement it your self, or use the following code:
//
// CMemDC.h header file
//
#pragma once
class CMemDC
{
public:
CMemDC(CDC& dc, CWnd* pWnd);
CMemDC(CDC& dc, const CRect& rect);
virtual ~CMemDC();
CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; }
BOOL IsMemDC() const { return m_bMemDC; }
BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; }
void EraseBkClip();
protected:
CDC& m_dc;
BOOL m_bMemDC;
HANDLE m_hBufferedPaint;
CDC m_dcMem;
CBitmap m_bmp;
CBitmap* m_pOldBmp;
CRect m_rect;
};
//
// CMemDC.cpp source file
//
#include "CMemDC.h"
CMemDC::CMemDC(CDC& dc, CWnd* pWnd) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL)
{
ASSERT_VALID(pWnd);
pWnd->GetClientRect(m_rect);
m_rect.right += pWnd->GetScrollPos(SB_HORZ);
m_rect.bottom += pWnd->GetScrollPos(SB_VERT);
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::CMemDC(CDC& dc, const CRect& rect) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL), m_rect(rect)
{
ASSERT(!m_rect.IsRectEmpty());
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::~CMemDC()
{
if (m_bMemDC)
{
CRect rectClip;
int nClipType = m_dc.GetClipBox(rectClip);
if (nClipType != NULLREGION)
{
if (nClipType != SIMPLEREGION)
{
rectClip = m_rect;
}
m_dc.BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), &m_dcMem, rectClip.left, rectClip.top, SRCCOPY);
}
m_dcMem.SelectObject(m_pOldBmp);
}
}
void CMemDC::EraseBkClip()
{
CRect clip;
m_dcMem.GetClipBox(&clip);
m_dcMem.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));
}
There is an ultra simple way I found that worked for me:
Turn off redraw with m_List1.SetRedraw(false)
Reset contents with m_List1.ResetContents()
Add new strings in loop with m_List1.AddString()
Then finalize by turning back on redraw and a m_List1.UpdateWindow().