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)
{
}
Related
I need to rotate certain elements of the UI (not all). I have tried a couple of things already and none of them have provided the solution I require.
I attempted to use the QGraphicsProxyWidget and Graphics View, this worked well for if I wanted to rotate the entire UI, but provided no way to rotate only certain elements.
I attempted to promote the widget to a custom class that overrides the paintEvent. I am unable to get this way to work though. The following is my current code
#include <QPushButton>
#include <QPainter>
#include <QPaintEvent>
class QRotateButton : public QPushButton
{
Q_OBJECT
public:
QRotateButton (QWidget *parent = 0) {}
protected:
void paintEvent(QPaintEvent *event) override {
if (isDrawing)
return QRotateButton::paintEvent(event);
isDrawing = true;
QPixmap buttonPixmap = grab();
QPainter painter(this);
painter.rotate(90);
painter.drawPixmap(0, 0, height(), width(), buttonPixmap);
isDrawing = false;
}
private:
bool isDrawing = false;
};
This returns the following errors
QWidget::repaint: Recursive repaint detected
Segmentation fault
Thanks in advance
Update
Following the comments below I changed the code to call QPushButton::paintEvent(Event) instead of QRotateButton::paintEvent(Event)
The application does now open, where as it didn't before. But unfortunately no button is shown. Whenever the paintEvent gets invoked now, the following error messages appear in the console:
QWidget::repaint: Recursive repaint detected
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::rotate: Painter not active
After playing around with this further, I managed to figure it out.
#include <QPushButton>
#include <QPainter>
#include <QPaintEvent>
class QRotateButton : public QPushButton
{
Q_OBJECT
public:
QRotateButton (QWidget *) {
//Set variables to defaults
_isDrawing = false;
_recursiveCounter = 0;
_recursiveMax = 1;
_rotationSetting = 0;
_rotationAngle = 0;
_rotationX = 0;
_rotationY = 0;
_rotationWidth = 0;
_rotationHeight = 0;
}
/* Set rotation setting */
void SetRotation(int setting) {
//Set rotation setting
_rotationSetting = setting;
//Call paintEvent
this->update();
}
protected:
void paintEvent(QPaintEvent *event) override {
//If its drawing then it just wants to do the normal paintEvent
if (_isDrawing)
{
QPushButton::paintEvent(event);
return;
}
//Setup the variables depending on setting
setupRotationVariables();
//Setup painter and rotate to angle
QPainter painter(this);
painter.rotate(_rotationAngle);
//Without this here, this function is called over and over,
//this stops that whilst maintaing the rotated image we want
if (_recursiveCounter > 0 && _requiresRotation)
{
_recursiveCounter--;
painter.drawPixmap(_rotationX, _rotationY, _drawing.width(), _drawing.height(), _drawing);
return;
}
//When rotating 90/270 degrees, we resize the image so that we don't stretch the image
//This is not required when flipping the image
if (_requiresRotation)
resize(_rotationWidth, _rotationHeight);
//Get the button image, this will call this function so we set _isDrawing to true whilst it's going on so meaning that it's does the base method and returns
_isDrawing = true;
_drawing = grab();
_isDrawing = false;
//Now we have the image, if rotation is being done resize the widget back to it's previous measurements
if (_requiresRotation)
resize(_rotationHeight, _rotationWidth);
//Draw the image to the picture
painter.drawPixmap(_rotationX, _rotationY, _drawing.width(), _drawing.height(), _drawing);
//Depending on if this has rotated or not, setup the catch the tailend of the rotation event
if (_requiresRotation)
_recursiveCounter = _recursiveMax;
else
_recursiveCounter = 0;
}
private:
/* Stops Recursive resizing when resizing is required */
int _recursiveCounter;
int _recursiveMax;
/* If the widget is currently drawing, stops certain recursive calls */
bool _isDrawing;
/* If the widget needs to rotated instead of flipped */
bool _requiresRotation;
/* Contains image of previous render of button, for use in recursive calls */
QPixmap _drawing;
/* Rotation Setting (0-3) defines rotation type. This is used since rotations should only be multiples of 90 */
int _rotationSetting;
/* Variables after rotation */
int _rotationAngle;
int _rotationX;
int _rotationY;
int _rotationWidth;
int _rotationHeight;
/* Fetch variables based on Rotation Setting */
void setupRotationVariables () {
//Figure out what rotation is required
switch (_rotationSetting)
{
case 0: //Up side up
_rotationAngle = 0;
_rotationWidth = width();
_rotationHeight = height();
_rotationX = 0;
_rotationY = 0;
_requiresRotation = false;
break;
case 1: //Right side up
_rotationAngle = 90;
_rotationWidth = height();
_rotationHeight = width();
_rotationX = 0;
_rotationY = -width();
_requiresRotation = true;
break;
case 2: //Down side up
_rotationAngle = 180;
_rotationWidth = width();
_rotationHeight = height();
_rotationX = -width();
_rotationY = -height();
_requiresRotation = false;
break;
case 3: //Left side up
_rotationAngle = 270;
_rotationWidth = height();
_rotationHeight = width();
_rotationX = -height();
_rotationY = 0;
_requiresRotation = true;
break;
default:
printf("INVALID ROTATION\n");
break;
}
}
};
Note
This will still throw QWidget::repaint: Recursive repaint detected every time paintEvent is called, but we expect this and it is handled in code. Whilst I would prefer it not show, I am unable to figure out any way to stop it.
I am trying to display a GIF picture as splash as starting of my small program in visual studio. I am really getting crazy. I looked it is possible in Qt IDE but I really need it in visual studio because my other code works only with visual studio. And yes I tried to convert my code for Qt giving me too many errors.
I have seen this post.
I am using GDI+ but still dunno how to simply display it instead of play and stop. It's okay even instead of splash to display a small form that plays the GIF file, can you guys give me a small code snippet in how to do it in c++?
Thanks.
Here is an MFC window class that implements a splash screen using GDI Plus for displaying an (animated) GIF.
I've encapsulated everything inside a header file to simplify using it with your project. Save it as an .h file (maybe "SplashWnd.h"), then include it wherever you want to set up the splash. You app's InitInstance might be a good place to add it - something like this line (before any DoModal calls):
SplashWnd splash(_T("Filname.gif"));
The constructor can also take parameters for controlling the delay before auto-closing, and also specifying a function to call when the splash is closed.
The splash window has no border or caption - it appears only as the loaded image, floating on top of any other windows. Its icon doesn't appear in the taskbar, and will close when its timeout expires, or the user clicks the window or presses a key.
#pragma once
#include <functional>
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")
inline void ManageGdiPlusInit(bool release=false) {
static int refcount = 0;
static ULONG_PTR token;
if(release) {
if(--refcount == 0) {
Gdiplus::GdiplusShutdown(token);
}
} else if(++refcount == 1) {
Gdiplus::GdiplusStartupInput startup_input;
Gdiplus::GdiplusStartup(&token, &startup_input, 0);
} }
inline void GdiPlusInit() { ManageGdiPlusInit(false); }
inline void GdiPlusRelease() { ManageGdiPlusInit(true); }
namespace {
class SplashWnd : public CWnd {
protected:
static CString WindowClass() {
static CString name;
if(name.IsEmpty()) {
name = AfxRegisterWndClass(CS_DROPSHADOW, 0, (HBRUSH)GetStockObject(GRAY_BRUSH), 0);
}
return name;
}
Gdiplus::Image *m_pImage;
UINT m_FrameCount;
unsigned char *m_FrameDelayData;
const UINT *m_FrameDelays;
UINT m_CurFrameIndex;
UINT m_AnimationTimerId;
UINT m_ExpireTimerId;
CRect m_WindowRect;
std::function<void()> m_DismissCallback;
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlags, CPoint point) {
DestroyWindow();
}
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
DestroyWindow();
}
afx_msg void OnDestroy() {
if(m_AnimationTimerId != UINT(-1)) {
KillTimer(m_AnimationTimerId);
}
if(m_ExpireTimerId != UINT(-1)) {
KillTimer(m_ExpireTimerId);
}
if(m_DismissCallback) {
m_DismissCallback();
}
CWnd::OnDestroy();
}
afx_msg void OnTimer(UINT nIDEvent) {
if(nIDEvent == m_AnimationTimerId) {
if(++m_CurFrameIndex >= m_FrameCount) {
m_CurFrameIndex = 0;
}
DrawCurFrame();
KillTimer(m_AnimationTimerId);
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
return;
}
if(nIDEvent == m_ExpireTimerId) {
DestroyWindow();
return;
} }
void PostNcDestroy() {
if(m_DeleteSelf) {
delete this;
}
}
void DrawCurFrame() {
Gdiplus::Graphics g(m_hWnd);
GUID dim_select_id = Gdiplus::FrameDimensionTime;
m_pImage->SelectActiveFrame(&dim_select_id, m_CurFrameIndex);
g.DrawImage(m_pImage, 0, 0, m_WindowRect.Width(), m_WindowRect.Height());
}
public:
// set m_DeleteSelf to true if a SplashWnd is created with new, and you want it to
// auto-delete itself when the window expires or is dismissed.
bool m_DeleteSelf;
// file_path the gif file path
// ExpireMs the time, in milliseconds until the window automatically closes itself
// WidthFactor the fraction of the width of the primary display to use as the splash screen's width
// HeightFactor the fraction of the height of the primary display to use as the height
// If WidthFactor or HeightFactor are 0, the original image aspect ratio is preserved
// If both are 0, the original image size, in pixels is used
SplashWnd(CString file_path, DWORD ExpireMs=2000, double WidthFactor=0.4, double HeightFactor=0) {
GdiPlusInit();
m_pImage = new Gdiplus::Image(file_path);
// Set up an array of frame times for animated images
UINT dimension_count = m_pImage->GetFrameDimensionsCount();
GUID dimension_id;
m_pImage->GetFrameDimensionsList(&dimension_id, 1);
m_FrameCount = m_pImage->GetFrameCount(&dimension_id);
UINT frame_delay_size = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_FrameDelayData = new unsigned char[frame_delay_size];
Gdiplus::PropertyItem* frame_delay_item = reinterpret_cast<Gdiplus::PropertyItem*>(m_FrameDelayData);
m_pImage->GetPropertyItem(PropertyTagFrameDelay, frame_delay_size, frame_delay_item);
m_FrameDelays = reinterpret_cast<const UINT*>(frame_delay_item->value);
// Figure out the size and location of the splash window
int primary_width = GetSystemMetrics(SM_CXFULLSCREEN);
int primary_height = GetSystemMetrics(SM_CYFULLSCREEN);
int splash_width = int(primary_width * WidthFactor);
int splash_height = int(primary_height * HeightFactor);
if(splash_width == 0) {
if(splash_height == 0) {
splash_width = m_pImage->GetWidth();
splash_height = m_pImage->GetHeight();
} else {
splash_width = primary_width * splash_height / primary_height;
}
} else if(splash_height == 0) {
splash_height = primary_height * splash_width / primary_width;
}
int l = (primary_width - splash_width) / 2;
int t = (primary_height - splash_height) / 2;
int r = l + splash_width;
int b = t + splash_height;
m_WindowRect.SetRect(
(primary_width - splash_width) / 2,
(primary_height - splash_height) / 2,
(primary_width + splash_width) / 2,
(primary_height + splash_height) / 2);
// WS_EX_TOPMOST makes the window cover up other, regular windows
// WS_EX_TOOLWINDOW prevents an icon for this window in the taskbar
// WS_POPUP prevents caption and border from being drawn
CreateEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, WindowClass(), _T("Splash"), WS_VISIBLE | WS_POPUP, m_WindowRect, 0, 0);
// Show the first frame
m_CurFrameIndex = 0;
DrawCurFrame();
// Set up the frame-flipping animation timer
m_ExpireTimerId = m_AnimationTimerId = UINT(-1);
if(m_FrameCount > 1) {
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
}
// Set up the expiration timer
if(ExpireMs != INFINITE) {
m_ExpireTimerId = SetTimer(2, ExpireMs, 0);
}
m_DeleteSelf = false;
}
// Constructor which takes a callback function which will be called when the splash window closes
template <typename F>
SplashWnd(CString file_path, DWORD ExpireMs, double WidthFactor, double HeightFactor, F DismissCallback)
: SplashWnd(file_path, ExpireMs, WidthFactor, HeightFactor)
{
m_DismissCallback = DismissCallback;
}
~SplashWnd() {
delete [] m_FrameDelayData;
delete m_pImage;
GdiPlusRelease();
}
};
// Message map, usually in an implementation file, but here encapsulated inside the header
// using an anonymous namespace to prevent possible ODR problems.
BEGIN_MESSAGE_MAP(SplashWnd, CWnd)
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_TIMER()
ON_WM_DESTROY()
END_MESSAGE_MAP()
}
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;
}
}
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();
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().