tags:

views:

84

answers:

3

Hi,

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.

A: 

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?

Troubadour
This was indeed not the case and I've changed the code now. However this did not change the issue. The code I've added looks like this:PictureView* image = new PictureView(path);image->Create("", SS_OWNERDRAW, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);Is there any way I can post this in a more readable format here?
Tekar
@Tom: You can add this information by just editing your original question.
Troubadour
A: 

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.

rlduffy
Possible but I can't get the DrawItem function to be called so I can't test this yet.
Tekar
My bad - apparently I can comment on my own answer. Livin' and learnin'.
rlduffy
You are correct, when WS_CHILD and WS_VISIBLE is added it works as a CStatic.
Tekar
A: 

Hi everybody, 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.

Tekar