CMemDC class issue - c++

Here is the following CMemDC class I am using from code projects that supposedly fixes the flickering:
#ifndef _MEMDC_H_
#define _MEMDC_H_
#include "stdafx.h"
namespace MemoryDC
{
class CMemDC : public CDC {
private:
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found in CMemDC
CDC* m_pDC; // Saves CDC passed in constructor
CRect m_rect; // Rectangle of drawing area.
BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.
public:
CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC()
{
ASSERT(pDC != NULL);
// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();
// Get the rectangle to draw
if (pRect == NULL) {
pDC->GetClipBox(&m_rect);
}
else {
m_rect = *pRect;
}
if (m_bMemDC) {
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);
SetMapMode(pDC->GetMapMode());
SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());
pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
}
else {
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}
// Fill background
FillSolidRect(m_rect, pDC->GetBkColor());
}
~CMemDC()
{
if (m_bMemDC) {
// Copy the offscreen bitmap onto the screen.
m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
this, m_rect.left, m_rect.top, SRCCOPY);
//Swap back the original bitmap.
SelectObject(m_oldBitmap);
}
else {
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}
// Allow usage as a pointer
CMemDC* operator->()
{
return this;
}
// Allow usage as a pointer
operator CMemDC*()
{
return this;
}
};
}
#endif
Here is my OnDraw function using above class:
void ViewerView::OnDraw(CDC* pDC)
{
MemoryDC::CMemDC dc(pDC);
ViewerDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
else
RenderPage(dc, 0);
}
And here is the OnEraseBkgnd function:
BOOL ViewerView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
Here is the following video which best describes what is happening to my scrolling since a video is worth a thousand words
I tried double buffering and what not to fix this issue, but I am not having a good time.
If anyone has any suggestions or help much is appreciated.
Salute.

Do not reinvent the wheel.
MFC already has a memory DC class, you can easily use it like this:
void ViewerView::OnDraw(CDC* pDC)
{
CRect rc;
GetClientRect(&rc);
CMemDC memDC(*pDC, rc);
auto& rDC = memDC.GetDC();
RenderPage(&rDC, 0);
}
As a sidenote:
If you don't want the system to erase the background for you every time, you should return TRUE from the OnEraseBkgnd function.

Related

Redraw shapes in MFC

I'm trying to develop a painter app using MFC with C++.
So I set up a view in which I do the actual painting. However, when I open the color button I get "leftovers" of the menu on my canvas view.. I have no idea how to erase them.. I tried using SaveDC and RestoreDC to restore it to the previous state but no luck there. As I understand it is meant to restore properties of the device context such as pen & brush but it has no use for me...
I also need this feature so when I put up a rectangle, I could present a preview for it, but the "previews` are again presented like leftovers.
My view's OnEraseBkgbd;
BOOL CanvasView::OnEraseBkgnd(CDC* pDC)
{
if (!isBackgroundInit)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(255, 255, 255)); // dialog background color
CBrush* pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
isBackgroundInit = true;
return bRes;
}
return 0;
}
My OnPaint:
void CanvasView::OnPaint()
{
CDialogEx::OnPaint();
//UpdateData(true);
CRect rect;
GetClientRect(&rect);
if (dc == nullptr)
{
dc = new CClientDC(this);
//dc->CreateCompatibleDC(dc);
HDC hdc = CreateCompatibleDC(*dc);
this->hdc = &hdc;
} else
{
BitBlt(*dc, 0, 0, (int)rect.Width(), (int)rect.Height(), *hdc, 0, 0, SRCCOPY);
}
}
How it looked at first:
How it looks when opening a color button:
How it looks after closing the color button menu(leftovers marked with red arrows):
When trying to put up a rectangle:
Any idea how to fix this and really restore drawing?
I am not sure I understand what you are trying to do.
CView has a virtual method called OnDraw. This is the method you must override:
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// first erase the entire client rectangle
CRect cr;
GetClientRect( &cr );
pDC->FillSolidRect( cr, RGB( 255, 255, 255 ) );
// your actual drawing goes here
pDC->Rectangle( 0, 0, 100, 100 );
}
You will also have to write OnEraseBkgnd:
BOOL CMyView::OnEraseBkgnd( CDC* /*pDC*/ )
{
return TRUE; // we fill the client rectangle in OnDraw
}
Also, it looks like you are trying to implement an ad-hoc memory DC. There is already one: CMemDC.

Setting individual items in a CListBox as bold with MFC

I stumbled over this article:
http://asg.unige.ch/Past/fuentes/Mfc/HowTo_44.html
So, I reproduced the class in my project:
// FontStyleListBox.cpp : implementation file
//
#include "stdafx.h"
#include "Meeting Schedule Assistant.h"
#include "FontStyleListBox.h"
// CFontStyleListBox
IMPLEMENT_DYNAMIC(CFontStyleListBox, CListBox)
CFontStyleListBox::CFontStyleListBox()
{
}
CFontStyleListBox::~CFontStyleListBox()
{
}
BEGIN_MESSAGE_MAP(CFontStyleListBox, CListBox)
ON_WM_DRAWITEM_REFLECT()
END_MESSAGE_MAP()
// CFontStyleListBox message handlers
void CFontStyleListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect;
// Draw the colored rectangle portion
rect.CopyRect(&lpDrawItemStruct->rcItem);
pDC->SetBkMode(TRANSPARENT);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
pDC->FillSolidRect(rect, GetSysColor(COLOR_HIGHLIGHT));
pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
pDC->FillSolidRect(rect, GetSysColor(COLOR_WINDOW));
pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
}
if ((int)(lpDrawItemStruct->itemID) >= 0) // Valid ID
{
CString sText;
int nIndex;
CFont newFont;
CFont *pOldFont;
nIndex = lpDrawItemStruct->itemID;
GetText(nIndex, sText);
FONTSTYLE fontStyle = (FONTSTYLE)GetItemData(nIndex);
// To avoid unnecessary processing
if (fontStyle == NORMAL) {
pDC->DrawText(sText, rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
return;
}
LOGFONT logFont;
CFont *pFont = GetFont();
pFont->GetLogFont(&logFont);
switch (fontStyle)
{
//case NORMAL: logFont.lfWeight = FW_NORMAL;
// break;
case BOLD: logFont.lfWeight = FW_BOLD;
break;
case ITALIC: logFont.lfItalic = TRUE;
break;
}
newFont.CreatePointFontIndirect(&logFont);
pOldFont = pDC->SelectObject(&newFont);
pDC->DrawText(sText, rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
pDC->SelectObject(pOldFont);
newFont.DeleteObject();
}
}
int CFontStyleListBox::GetFontStyle(int nIndex)
{
return (FONTSTYLE)GetItemData(nIndex);
}
void CFontStyleListBox::SetFontStyle(int nIndex, FONTSTYLE fontStyle)
{
SetItemData(nIndex, (DWORD)fontStyle);
Invalidate();
}
I then used it in my application. I set the properties correctly for ownerdraw etc. but here is the results:
The bold entry is the last one. Why is it not rendering correctly?
How do I fix this and / or are there newer ways to get this done?
You just need to CFont::CreateFontIndirect use. Using CFont::CreatePointFontIndirect causes a conversion from the font points into physical points. You don't need that.
Also create the font only once. You may create it on demand in DrawItem. Just create a member in your subclassed CListBox...

Memory resource usage goes way up while resizing(by dragging) a dialog window in mfc

I have got a problem about the memory resource usage getting very high while resizing (by dragging) a dialog box.
I've made a view on the dialog and done show bmp image in view.
But when I was resizing (by dragging) the dialog so many times, the memory resource usage increased very high. I don't know where to start from.
What should I be checking?
My code concept as following.
CTestView::OnDraw(CDC * pDC)
{
...
DodisplayImage();
...
}
Void CTestView::DodisplayImage(void)
{
if (m_pImage == NULL) { return; }
PixelPacket *pPixels;
CPoint pt;
CRect rectClient;
CDC * pDC;
pDC = GetDC();
GetClientRect(rectClient);
if (pDC != NULL) {
int nImageY;
int nImageX;
CSize sizeScaled; // Clear the background pDC->FillSolidRect(rectClient,pDC->GetBkColor());
...
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); bmi.biWidth = m_pImage->columns();
bmi.biHeight = (-1)*m_pImage->rows(); bmi.biPlanes = 1; bmi.biBitCount = 32;
...
,,BitBlt (...);
DeleteObject(hMemDC);
}
I've followed dodisplayimage() at the like following link
ftp://ftp.mpe.mpg.de/pub/ImageMagick/ImageMagick-5.3.3/contrib/win32/MFC/NtMagick/NtMagickView.cpp
There is a leak here:
Void CTestView::DodisplayImage()
{
CDC *pDC = GetDC();
pDC->FillSolidRect(rect, pDC->GetBkColor());
ReleaseDC(pDC); //<= needs cleanup
}
ReleaseDC must be called to clean up after GetDC. See also documentation for GetDC()
Or you can just avoid all that because MFC has classes with automatic cleanup. Replace GetDC with CClientDC every where in the code:
Void CTestView::DodisplayImage()
{
CClientDC dc(this)
dc.FillSolidRect(rect, dc.GetBkColor());
//do stuff with HDC hdc = dc.GetSafeHdc(); ...
//or CDC *pDC = &dc;
}

GetTextExtentPoint32(m_hAttribDC, lpszString, nCount, &size))

My program is to display some points with their positions(x,y) on a graph. When I use mouse to drag any point, its position will automatically changed. Updated position is implemented following this code(using thread):
m_thread =AfxBeginThread((AFX_THREADPROC)MainThread,this)
UINT CAtwWnd::MainThread(LPVOID pParam)
{
CAtwWnd *pMainDlg = (CAtwWnd*)pParam;
static SChartXYPoint pPoint;
TCHAR strTemp[32]={0,};
while(1)
{
pMainDlg->m_chart.EnableRefresh(false);
pMainDlg->InitGraph1();
wsprintf(strTemp, _T("[%d](%d,%d)"), (int)index,(int)xPoint,(int) yPoint);
pBalloon[index]->SetLabelText(strTemp);
pBalloon[index] = pMainDlg->m_pPointSeries->CreateBalloonLabel(index, strTemp);
pBalloon[index]->SetVisisble(true);
pMainDlg->m_pPointSeries->SetVisible(true);
pMainDlg->m_chart.EnableRefresh(true);
pMainDlg->SetAtwGraphStep(1);
}
return 0;
}
Mean while:
void CChartLabel<PointType>::SetLabelText(const TChartString& strText)
{
m_strLabelText = strText;
m_pParentCtrl->RefreshCtrlAtw();
}
And:
void CChartCtrl::RefreshCtrlAtw()
{
// Window is not created yet, so skip the refresh.
if (!GetSafeHwnd())
return;
if (m_iEnableRefresh < 1)
{
m_bPendingRefresh = true;
return;
}
// Retrieve the client rect and initialize the
// plotting rect
CClientDC dc(this) ;
CRect ClientRect;
GetClientRect(&ClientRect);
m_PlottingRect = ClientRect;
// If the backgroundDC was not created yet, create it (it
// is used to avoid flickering).
if (!m_BackgroundDC.GetSafeHdc() )
{
CBitmap memBitmap;
m_BackgroundDC.CreateCompatibleDC(&dc) ;
memBitmap.CreateCompatibleBitmap(&dc, ClientRect.Width(),ClientRect.Height()) ;
m_BackgroundDC.SelectObject(&memBitmap) ;
}
// Draw the chart background, which is not part of
// the DrawChart function (to avoid a background when
// printing).
DrawBackground(&m_BackgroundDC, ClientRect);
ClientRect.DeflateRect(3,3);
DrawChart(&m_BackgroundDC,ClientRect);
for (int i=0; i<4 ;i++)
{
if (m_pAxes[i])
m_pAxes[i]->UpdateScrollBarPos();
}
Invalidate();
}
when dragging points on graph I gets these errors somtimes: Debug Assertion Failed ( afxwin1.inl, line 639, and 646)
_AFXWIN_INLINE CSize CDC::GetTextExtent(LPCTSTR lpszString, int nCount) const
{
ASSERT(m_hAttribDC != NULL);
SIZE size;
VERIFY(::GetTextExtentPoint32(m_hAttribDC, lpszString, nCount, &size));
return size;
}
_AFXWIN_INLINE CSize CDC::GetTextExtent(const CString& str) const
{
ASSERT(m_hAttribDC != NULL);
SIZE size;
VERIFY(::GetTextExtentPoint32(m_hAttribDC, str, (int)str.GetLength(), &size));
return size;
}
Could you help me to fix this problem? I tried to find some ways to fix but doesn't work. :(
My answer is just a guess, but the reason might be caused by using MFC objects from one thread (the creator) in a second thread. And it is a guess because you didn't told us what the ASSERT say and what VS version you are using.
The problem: When you create some objects in the MFC, the handle values are saved in a map that allows the MFC to find the object only with the handle value.
This handle maps are stored per thread.
Also if a window object stores other objects that are associated with those handle maps, the usage from another thread will fail.
So the answer can be found in the call stack. It tells you who uses such an object. And the object that causes the problem is simply identified by the ASSERT.

Drawing a jpg in MFC

I've been trying to show a jpg image in MFC, but I can't get it drawn. I'm a complete MFC newbie an everything I got up until now is mostly adapted from things I found on the net. Currently I have this:
Picture.h:
#pragma once
#include <afxwin.h>
class Picture
{
public:
Picture();
bool load(LPCTSTR filePath);
bool draw( CDC* deviceContext
, CRect clientRect
, LPCRECT prcMFBounds);
CSize getSize(CDC* pDC);
private:
LPPICTURE m_picture;
};
Picture.cpp:
#include "Picture.h"
Picture::Picture()
: m_picture(0)
{
}
bool Picture::load(LPCTSTR szFile)
{
HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false;
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == (DWORD)-1)
{
CloseHandle(hFile);
return false;
}
LPVOID pvData = NULL;
// alloc memory based on file size
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
if (hGlobal == NULL)
{
CloseHandle(hFile);
return false;
}
pvData = GlobalLock(hGlobal);
if (pvData == NULL)
{
GlobalUnlock(hGlobal);
CloseHandle(hFile);
return false;
}
DWORD dwBytesRead = 0;
// read file and store in global memory
bool bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL) != 0;
GlobalUnlock(hGlobal);
CloseHandle(hFile);
if (!bRead)
return false;
LPSTREAM pstm = NULL;
// create IStream* from global memory
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
if (!(SUCCEEDED(hr)))
{
if (pstm != NULL)
pstm->Release();
return false;
}
else if (pstm == NULL)
return false;
// Create IPicture from image file
if (m_picture)
m_picture->Release();
hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&(m_picture));
if (!(SUCCEEDED(hr)))
{
pstm->Release();
return false;
}
else if (m_picture == NULL)
{
pstm->Release();
return false;
}
pstm->Release();
return true;
}
bool Picture::draw(CDC* deviceContext, CRect clientRect, LPCRECT prcMFBounds)
{
if (clientRect.IsRectNull())
{
CSize imageSize = getSize(deviceContext);
clientRect.right = imageSize.cx;
clientRect.bottom = imageSize.cy;
}
long pictureWidth = 0;
long pictureHeigth = 0;
m_picture->get_Width(&pictureWidth);
m_picture->get_Height(&pictureHeigth);
m_picture->Render( *deviceContext
, clientRect.left
, clientRect.top
, clientRect.Width()
, clientRect.Height()
, 0
, pictureHeigth
, pictureWidth
, -pictureHeigth
, prcMFBounds);
return true;
}
CSize Picture::getSize(CDC* deviceContext)
{
if (!m_picture)
return CSize(0,0);
LONG width, height; // HIMETRIC units
m_picture->get_Width(&width);
m_picture->get_Height(&height);
CSize size(width, height);
if (deviceContext==NULL)
{
CWindowDC dc(NULL);
dc.HIMETRICtoDP(&size); // convert to pixels
}
else
{
deviceContext->HIMETRICtoDP(&size);
}
return size;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CStatic
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
PictureView.cpp:
#include "PictureView.h"
#include "Picture.h"
PictureView::PictureView(std::string path)
{
m_picture = new Picture();
m_picture->load(path.c_str());
}
void PictureView::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
{
CRect rect;
GetClientRect(&rect);
m_picture->draw(GetDC(), rect, rect);
}
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", SS_OWNERDRAW, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
The problem is that I can't get the DrawItem function to be called. What am I doing wrong?
Any help will be appreciated.
Here's an easy way to draw an image in an MFC dialog box:
link with gdiplus.dll
#include "atlimage.h>
#include "gdiplus.h>
using namespace Gdiplus
.
.
.
CImage ci;
ci.Load((CString)"D:\\Pictures\\mycat.jpg");
CDC *dc = AfxGetMainWnd()->GetDC();
HDC hdc = *dc;
ci.Draw(hdc,10,10);
Hope it works!
with the help of a colleague of mine I've found the answer. He told me it's not possible to have an ownerdrawn CStatic. So when I now inherit PictureView from CButton and make it BS_OWNERDRAW my image is rendered.
Personally I think this is an ugly solution but I'm so tired of this problem now that I don't really care that much. These are the changes I've made to make it work:
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CButton
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
Thank's everybody for all the help.
I'm not sure that the GetDC in the call to m_picture->draw will necessarily refer to the same DC that's given in the CREATESTRUCT. What I've done is:
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
EDIT: Since I can't comment, hopefully an edit will suffice. I think your colleague is mistaken, as I just recently implemented a CStatic control using SS_OWNERDRAW. I'm guessing that the addition of WS_CHILD | WS_VISIBLE on the Create call is key.
I've never used MFC but a quick perusal of the CStatic::DrawItem documentation says that it must be created with the SS_OWNERDRAW style for DrawItem to get called. You haven't shown the Create line for your PictureView so perhaps it's that?