Related
I have a class, which I tried in another MFC project which compiled fine, but for some reason in this project it doesn't compile and complains about my wndproc function that is declared in a header file.
Below is the header file of the class I used in another MFC project. I had to comment out #include <windows.h> because it would say it can't be used in MFC, which wasn't the case in the other MFC project (what must be noted is that the other MFC project had precompiled headers).
skin.h
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.h : CSkin class declaration
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#ifndef _SKIN_H_
#define _SKIN_H_
//#include <windows.h>
#include <afxwin.h>
// --------------------------------------------------------------------------
// The CSkin class will load the skin from a resource
// and subclass the associated window, so that the
// WM_PAINT message will be redirected to the provided
// window procedure. All the skin handling will be automatized.
// --------------------------------------------------------------------------
class CSkin
{
// --------------------------------------------------------------------------
// the skin window procedure, where the class
// will handle WM_PAINT and WM_LBUTTONDOWN automatically.
// --------------------------------------------------------------------------
friend LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
private:
// the associated window handle
HWND m_hWnd;
// the associated CWnd window handle
CWnd * m_cwnd;
// the old window procedure
WNDPROC m_OldWndProc;
// skin region
HRGN m_rgnSkin;
// the internal skin device context handle
HDC m_dcSkin;
// bitmap and old bitmap from the device context
HBITMAP m_hBmp, m_hOldBmp;
// skin dimensions
int m_iWidth, m_iHeight;
// on|off toggle
bool m_bEnabled;
// tell the class if it has a window subclassed.
bool m_bHooked;
// skin retrieval helper
bool GetSkinData(int iSkinRegion, int iSkinBitmap);
public:
// ----------------------------------------------------------------------------
// constructor 1 - use it when you have not already created the app window.
// this one will not subclass automatically, you must call Hook() to subclass.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin(int iSkinRegion, int iSkinBitmap);
// ----------------------------------------------------------------------------
// constructor 2 - use it when you have already created the app window.
// this one will subclass the window automatically.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap);
// ----------------------------------------------------------------------------
// destructor - just call the destroyer
// ----------------------------------------------------------------------------
virtual ~CSkin();
// ----------------------------------------------------------------------------
// destroy skin resources and free allocated resources
// ----------------------------------------------------------------------------
void Destroy();
// ----------------------------------------------------------------------------
// subclass a window.
// ----------------------------------------------------------------------------
bool Hook(HWND hWnd);
// ----------------------------------------------------------------------------
// subclass a window. The CWnd version
// ----------------------------------------------------------------------------
bool Hook(CWnd *cwnd);
// ----------------------------------------------------------------------------
// unsubclass the subclassed window.
// ----------------------------------------------------------------------------
bool UnHook();
// ----------------------------------------------------------------------------
// unsubclass the subclassed window. The CWnd version
// ----------------------------------------------------------------------------
bool UnHookCWnd();
// ----------------------------------------------------------------------------
// tell us if we have a window subclassed.
// ----------------------------------------------------------------------------
bool Hooked();
// ----------------------------------------------------------------------------
// toggle skin on/off.
// ----------------------------------------------------------------------------
bool Enable(bool bEnable);
// ----------------------------------------------------------------------------
// tell if the skinning is enabled
// ----------------------------------------------------------------------------
bool Enabled();
// ----------------------------------------------------------------------------
// return the skin bitmap width.
// ----------------------------------------------------------------------------
int Width();
// ----------------------------------------------------------------------------
// return the skin bitmap height.
// ----------------------------------------------------------------------------
int Height();
// ----------------------------------------------------------------------------
// return the skin device context.
// ----------------------------------------------------------------------------
HDC HDC();
// ----------------------------------------------------------------------------
// return the handle region.
// ----------------------------------------------------------------------------
HRGN HRGN();
// toggle skin on/off for Cwnd
bool EnableCWnd(bool bEnable);
};
#endif
This is the cpp file:
skin.cpp
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.cpp : CSkin class implementation
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "skin.h"
// ----------------------------------------------------------------------------
// constructor 1 - use it when you have not already created the app window.
// this one will not subclass automatically, you must call Hook() and Enable()
// to subclass the app window and enable the skin respectively.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin::CSkin(int iSkinRegion, int iSkinBitmap)
{
// default starting values
m_bHooked = false;
m_OldWndProc = NULL;
// try to retrieve the skin data from resource.
if ( !GetSkinData(iSkinRegion, iSkinBitmap) )
throw ("Unable to retrieve the skin.");
}
// ----------------------------------------------------------------------------
// constructor 2 - use it when you have already created the app window.
// this one will subclass the window and enable the skin automatically.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin::CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap)
{
// default starting values
m_bHooked = false;
m_OldWndProc = NULL;
// initialize
CSkin(iSkinRegion, iSkinBitmap);
// subclass
Hook(hWnd);
// enable
Enable(true);
}
// ----------------------------------------------------------------------------
// destructor - just call the destroyer
// ----------------------------------------------------------------------------
CSkin::~CSkin()
{
Destroy();
}
// ----------------------------------------------------------------------------
// destroy skin resources and free allocated resources
// ----------------------------------------------------------------------------
void CSkin::Destroy()
{
// unhook the window
UnHook();
// free bitmaps and device context
if (m_dcSkin) { SelectObject(m_dcSkin, m_hOldBmp); DeleteDC(m_dcSkin); m_dcSkin = NULL; }
if (m_hBmp) { DeleteObject(m_hBmp); m_hBmp = NULL; }
// free skin region
if (m_rgnSkin) { DeleteObject(m_rgnSkin); m_rgnSkin = NULL; }
}
// ----------------------------------------------------------------------------
// toggle skin on/off - must be Hooked() before attempting to enable skin.
// ----------------------------------------------------------------------------
bool CSkin::Enable(bool bEnable)
{
// refuse to enable if there is no window subclassed yet.
if (!Hooked()) return false;
// toggle
m_bEnabled = bEnable;
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
return true;
}
// ----------------------------------------------------------------------------
// tell if the skinning is enabled
// ----------------------------------------------------------------------------
bool CSkin::Enabled()
{
return m_bEnabled;
}
// ----------------------------------------------------------------------------
// hook a window
// ----------------------------------------------------------------------------
bool CSkin::Hook(HWND hWnd)
{
// unsubclass any other window
if (Hooked()) UnHook();
// this will be our new subclassed window
m_hWnd = hWnd;
// set the skin region to the window
SetWindowRgn(m_hWnd, m_rgnSkin, true);
// subclass the window procedure
m_OldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)SkinWndProc);
// store a pointer to our class instance inside the window procedure.
if (!SetProp(m_hWnd, "skin", (void*)this))
{
// if we fail to do so, we just can't activate the skin.
UnHook();
return false;
}
// update flag
m_bHooked = ( m_OldWndProc ? true : false );
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
// successful return if we're hooked.
return m_bHooked;
}
bool CSkin::Hook(CWnd *cwnd)
{
if (Hooked() )UnHookCWnd();
m_cwnd = cwnd;
m_cwnd->SetWindowRgn(m_rgnSkin, TRUE);
m_bHooked = true;
m_cwnd->InvalidateRect(NULL, TRUE);
//m_cwnd->Invalidate(true);
//m_cwnd->UpdateWindow();
return m_bHooked;
}
// ----------------------------------------------------------------------------
// unhook the window
// ----------------------------------------------------------------------------
bool CSkin::UnHook()
{
// just to be safe we'll check this
WNDPROC OurWnd;
// cannot unsubclass if there is no window subclassed
// returns true anyways.
if (!Hooked()) return true;
// remove the skin region from the window
SetWindowRgn(m_hWnd, NULL, true);
// unsubclass the window procedure
OurWnd = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_OldWndProc);
// remove the pointer to our class instance, but if we fail we don't care.
RemoveProp(m_hWnd, "skin");
// update flag - if we can't get our window procedure address again,
// we failed to unhook the window.
m_bHooked = ( OurWnd ? false : true );
// force window repainting
InvalidateRect(m_hWnd, NULL, TRUE);
// successful return if we're unhooked.
return !m_bHooked;
}
bool CSkin::UnHookCWnd()
{
if (!Hooked()) return true;
m_cwnd->SetWindowRgn(NULL, TRUE);
m_bHooked = false;
m_cwnd->InvalidateRect(NULL, TRUE);
return !m_bHooked;
}
// ----------------------------------------------------------------------------
// tell us if there is a window subclassed
// ----------------------------------------------------------------------------
bool CSkin::Hooked()
{
return m_bHooked;
}
// ----------------------------------------------------------------------------
// return the skin bitmap width
// ----------------------------------------------------------------------------
int CSkin::Width()
{
return m_iWidth;
}
// ----------------------------------------------------------------------------
// return the skin bitmap height
// ----------------------------------------------------------------------------
int CSkin::Height()
{
return m_iHeight;
}
// ----------------------------------------------------------------------------
// return the skin device context
// ----------------------------------------------------------------------------
HDC CSkin::HDC()
{
return m_dcSkin;
}
HRGN CSkin::HRGN()
{
return m_rgnSkin;
}
// ----------------------------------------------------------------------------
// skin retrieval helper
// ----------------------------------------------------------------------------
bool CSkin::GetSkinData(int iSkinRegion, int iSkinBitmap)
{
// get app instance handle
HINSTANCE hInstance = GetModuleHandle(NULL);
// -------------------------------------------------
// retrieve the skin bitmap from resource.
// -------------------------------------------------
m_hBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(iSkinBitmap));
if (!m_hBmp) return false;
// get skin info
BITMAP bmp;
GetObject(m_hBmp, sizeof(bmp), &bmp);
// get skin dimensions
m_iWidth = bmp.bmWidth;
m_iHeight = bmp.bmHeight;
// -------------------------------------------------
// then, we retrieve the skin region from resource.
// -------------------------------------------------
// ask resource for our skin.
HRSRC hrSkin = FindResource(hInstance, MAKEINTRESOURCE(iSkinRegion),"BINARY");
if (!hrSkin) return false;
// this is standard "BINARY" retrieval.
LPRGNDATA pSkinData = (LPRGNDATA)LoadResource(hInstance, hrSkin);
if (!pSkinData) return false;
// create the region using the binary data.
m_rgnSkin = ExtCreateRegion(NULL, SizeofResource(NULL,hrSkin), pSkinData);
// free the allocated resource
FreeResource(pSkinData);
// check if we have the skin at hand.
if (!m_rgnSkin) return false;
// -------------------------------------------------
// well, things are looking good...
// as a quick providence, just create and keep
// a device context for our later blittings.
// -------------------------------------------------
// create a context compatible with the user desktop
m_dcSkin = CreateCompatibleDC(0);
if (!m_dcSkin) return false;
// select our bitmap
m_hOldBmp = (HBITMAP)SelectObject(m_dcSkin, m_hBmp);
// -------------------------------------------------
// done
// -------------------------------------------------
return true;
}
// ------------------------------------------------------------------------
// Default skin window procedure.
// Here the class will handle WM_PAINT and WM_LBUTTONDOWN, originally sent
// to the application window, but now subclassed. Any other messages will
// just pass through the procedure and reach the original app procedure.
// ------------------------------------------------------------------------
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
// we will need a pointer to the associated class instance
// (it was stored in the window before, remember?)
CSkin *pSkin = (CSkin*)GetProp(hWnd, "skin");
// to handle WM_PAINT
PAINTSTRUCT ps;
// if we fail to get our class instance, we can't handle anything.
if (!pSkin) return DefWindowProc(hWnd,uMessage,wParam,lParam);
switch(uMessage)
{
case WM_PAINT:
{
// ---------------------------------------------------------
// here we just need to blit our skin
// directly to the device context
// passed by the painting message.
// ---------------------------------------------------------
BeginPaint(hWnd,&ps);
// blit the skin
BitBlt(ps.hdc,0,0,pSkin->Width(),pSkin->Height(),pSkin->HDC(),0,0,SRCCOPY);
EndPaint(hWnd,&ps);
break;
}
case WM_LBUTTONDOWN:
{
// ---------------------------------------------------------
// this is a common trick for easy dragging of the window.
// this message fools windows telling that the user is
// actually dragging the application caption bar.
// ---------------------------------------------------------
SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
break;
}
}
// ---------------------------------------------------------
// call the default window procedure to keep things going.
// ---------------------------------------------------------
return CallWindowProc(pSkin->m_OldWndProc, hWnd, uMessage, wParam, lParam);
}
// toggle skin on/off for Cwnd
bool CSkin::EnableCWnd(bool bEnable)
{
// TODO: Add your implementation code here.
if (!Hooked()) return false;
// toggle
m_bEnabled = bEnable;
m_cwnd->InvalidateRect(NULL, TRUE);
//m_cwnd->Invalidate(TRUE);
//m_cwnd->UpdateWindow();
return true;
}
Here are my other files, if you think they will be relevant:
Hello.h
#pragma once
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow : public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
// This is the height of the bitmap
int iHeight;
int iWidth;
int BitmapId;
// This sets the height variable of the bitmap
void SetHeight(int height);
// This sets the width variable of the bitmap
void SetWidth(int width);
// This is sets the bitmap id for the image
void SetBitmapId(int bitmapid);
};
Hello.cpp
#include <afxwin.h>
#include "Hello.h"
#include "SkinClass/skin.h"
#include "resource.h"
CMyApp myApp;
/////////////////////////////////////////////////////////////////////////
// CMyApp member functions
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMainWindow;
CSkin ii(ID_bitmap, ID_REGION);
CMainWindow* cmw = dynamic_cast<CMainWindow*>(m_pMainWnd);
cmw->SetHeight(ii.Height());
cmw->SetWidth(ii.Width());
cmw->SetBitmapId(ID_bitmap);
ii.Hook(m_pMainWnd);
ii.Enable(true);
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_PAINT()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
Create(NULL, _T("The Hello Application"));
}
void CMainWindow::OnPaint()
{
//CPaintDC dc(this);
//CRect rect;
//GetClientRect(&rect);
//dc.DrawText(_T("Hello, MFC"), -1, &rect,
// DT_SINGLELINE | DT_CENTER | DT_VCENTER);
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CFrameWnd::OnPaint() for painting messages
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bm;
bm.LoadBitmap(BitmapId);
memDC.SelectObject(bm);
dc.BitBlt(0, 0, iWidth, iHeight, &memDC, 0, 0, SRCCOPY);
}
void CMainWindow::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
// if (nChar == VK_ESCAPE) {
// MessageBox("we have entered here");
// //std::cout << "some stuff in here";
//}
if (nChar == VK_ESCAPE) {
PostQuitMessage(0);
}
CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
// This sets the height variable of the bitmap
void CMainWindow::SetHeight(int height)
{
// TODO: Add your implementation code here.
iHeight = height;
}
// This sets the width variable of the bitmap
void CMainWindow::SetWidth(int width)
{
// TODO: Add your implementation code here.
iWidth = width;
}
// This is sets the bitmap id for the image
void CMainWindow::SetBitmapId(int bitmapid)
{
// TODO: Add your implementation code here.
BitmapId = bitmapid;
}
The error message I am getting is:
Error C2065 'SkinWndProc': undeclared identifier mfcexample2 C:\Users\samsu_kag5dbu\source\repos\mfcexample2\mfcexample2\SkinClass\skin.cpp 134
Per https://en.cppreference.com/w/cpp/language/friend :
A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at namespace scope is provided - see namespaces for details.
You are declaring SkinWndProc() for the first time in skin.h as a friend of CSkin, and then both declaring and defining the actual SkinWndProc() function only in skin.cpp after the definition of CSkin::Hook() tries to use it. As such, SkinWndProc() has not actually been declared yet in the namespace that owns CSkin, so it is not visible to Hook(), which is why you are getting an "undeclared identifier" error.
To fix this, you can either:
in skin.cpp, add a forward declaration for SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
bool CSkin::Hook(HWND hWnd)
{
// can now use SkinWndProc as needed...
}
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}
in skin.cpp, move the entire definition of SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}
bool CSkin::Hook(HWND hWnd)
{
// can now use SkinWndProc as needed...
}
in skin.h and skin.cpp, change SkinWndProc() to be a static class method instead of a standalone friend function, eg:
class CSkin
{
static LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
...
};
bool CSkin::Hook(HWND hWnd)
{
// can use CSkin::SkinWndProc as needed...
}
LRESULT CALLBACK CSkin::SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
...
}
I try to display a bitmap (gray image) by using MFC but failed. I am using standard windows API in my MFC codes. Below is the code (view.cpp and I put own code in ::OnDraw()):
lpBits is the BYTE array contains the bitmap BYTE data.
When I run the code, I got an "exception thrown" error message and no display of the bitmap (a black bitmap image).
Below you can find the code as well as the error message.
extern BYTE lpBits[510][1024 * 4] = { 0 };
extern CRITICAL_SECTION g_cs;
View.h:
class Cmfc_gui_test1View : public CView
{
protected: // create from serialization only
Cmfc_gui_test1View();
DECLARE_DYNCREATE(Cmfc_gui_test1View)
// Attributes
public:
Cmfc_gui_test1Doc* GetDocument() const;
// Operations
public:
// Overrides
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
// Implementation
public:
virtual ~Cmfc_gui_test1View();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
afx_msg void OnFilePrintPreview();
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
DECLARE_MESSAGE_MAP()
public:
CBitmap* pBitmap;
CDC* pcdcMem;
CDC* m_pDC;
CRect crect;
CWnd* pwndParent;
int screenX;
int screenY;
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};
View.cpp:
// Cmfc_gui_test1View drawing
void Cmfc_gui_test1View::OnDraw(CDC* pDC)
{
Cmfc_gui_test1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
screenX = GetSystemMetrics(SM_CXSCREEN);
screenY = GetSystemMetrics(SM_CYSCREEN);
pBitmap->CreateCompatibleBitmap(m_pDC, 1024, 510); // Error here
pBitmap->SetBitmapBits(510*1024*4, lpBits);
pcdcMem->CreateCompatibleDC(m_pDC);
pcdcMem->SelectObject(pBitmap);
m_pDC->StretchBlt( 0, 0, screenX, screenY, pcdcMem, 0, 0, 1024, 510, SRCCOPY);
pDC->DeleteDC();
}
// Cmfc_gui_test1View message handlers
int Cmfc_gui_test1View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
m_pDC = new CClientDC(this);
return 0;
}
Error message occurs when execution hits the line pBitmap->CreateCompatibleBitmap(m_pDC, 1024, 510);:
0x0F7C0AEA (mfc140d.dll)处(位于 mfc_gui_test1.exe 中)引发的异常: 0xC0000005: 读取位置 0xCDCDCDD1 时发生访问冲突。
如有适用于此异常的处理程序,该程序便可安全地继续运行。
Translated:
Exception thrown at 0x0F7C0AEA (mfc140d.dll) (in mfc_gui_test1.exe): 0xC0000005: Access violation while reading location 0xCDCDCDD1.
If there is a handler for this exception, the program can safely continue running.
Problem Dsecription: Application to show an editable table in new window. e.g.: In the following table, I want to be able to edit the register values. I tried to write the application using MFC on Visual Studio 2015 in C++
===========================================================================
I'm working on a MFC application using Visual Studio 2015 in C++
I've created a Dialog Editor In which I want to show a list of registers (type list control) with two columns, one that specifies the registers numbers and a second column to show their values. I've started with the simplest case, and created such list with only one register successfully with the following code:
.cpp file:
#include "stdafx.h"
#include "MFCApplication4.h"
#include "MFCApplication4Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
CMFCApplication4Dlg::CMFCApplication4Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_MFCAPPLICATION4_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication4Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, listCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication4Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CMFCApplication4Dlg::OnLvnItemchangedList1)
END_MESSAGE_MAP()
BOOL CMFCApplication4Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
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);
}
}
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
listCtrl.InsertColumn(0, _T("Register Number"));
listCtrl.InsertColumn(1, _T("Register Value"));
listCtrl.SetColumnWidth(0, 200);
listCtrl.SetColumnWidth(1, 400);
int nItem = listCtrl.InsertItem(0, L"0x00");
listCtrl.SetItemText(nItem, 1, L"01000001");
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication4Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
void CMFCApplication4Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
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;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CMFCApplication4Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMFCApplication4Dlg::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
.h file:
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
class CMFCApplication4Dlg : public CDialogEx
{
public:
CMFCApplication4Dlg(CWnd* pParent = NULL); // standard constructor
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION4_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
protected:
HICON m_hIcon;
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult);
CListCtrl listCtrl;
};
I would like to be able to edit the value of the register by clicking on it once or twice and typing the new value. I tried adding the following function to the header beneath the last line of CListCtrl listCtrl:
afx_msg void RightButtonClick(WPARAM wParam, LPARAM lParam, CPoint point);
the implementation of the function in .cpp file:
afx_msg void CMFCApplication4Dlg::RightButtonClick(WPARAM wParam, LPARAM lParam, CPoint point)
{
//listCtrl.SetFocus();
//CEdit* itemToEdit = listCtrl.EditLabel(1);
// The string replacing the text in the edit control.
LPCTSTR lpszmyString = _T("custom label!");
// If possible, replace the text in the label edit control.
CEdit* pEdit = listCtrl.GetEditControl();
if (pEdit != NULL)
{
pEdit->SetWindowText(lpszmyString);
}
}
and I added the following message to the message map in the .cpp file (last line in the block BEGIN_MESSAGE_MAP):
ON_WM_RBUTTONDBLCLK(LVN_ENDLABELEDIT, IDC_LIST1, &CMFCApplication4Dlg::RightButtonClick)
but unfortunately that's what I'm getting no results.
I've tried reading some more, and I've spent few hours trying to fix it, but haven't succeeded. I've tried to follow the answers to similar posts which are listed below:
Make single items editable in a list control (C++, MFC)
and
How to edit cell in listcontrol mfc?
but I wasn't able to implement the suggestions that were written there, I didn't understand how to connect all the parts of the answers. Additionally, I tried using this guide:
https://www.tutorialspoint.com/mfc/mfc_messages_events.htm
Unfortunately, nothing helped. I have a feeling that I'm missing something fundamental, perhaps I'm not handling the messages as I should. I'd appreciate it if anyone could explain to me what I'm missing and how to fix it.
Please let me know if my question isn't clear enough or if there's any other problem with it, so I'll edit it and get better.
Thank you very much for your time and attention.
I used this guide which solved my problem
I'm trying to define new colors in some regions of a CPropertySheet (mfc libaries). What I've tried is to overload OnCtlColorand define a new background color. This methods works well but it doesn't colorize the region I want.
In the next image you can see what I get with my method.
Image of the control
In this image you can see 4 colorized regions:
Red: Region that I can colorize using OnCtlColor
Dark Gray and black: Region that I can colorize using OnCtlColor
of the object CPropertyPage
Light Gray (Indicated with a blue arrow): Region I want to colorize
White margin: Region I want to colorize too.
I don't know what to do to colorize all regions using this libraries or using any Customizable object. Any help will be appreciated.
Thanks!
Update 1
After the answer of Adrian it looks like this
However, there's still one region we cannot colorize.
Answer
Before trying a lot of combinations, I've done the next two objects which allows me to define the colors I need. You can find all source code behind. The result of this code can be checked in this picture
PropertyPage
Header
class CustomPropertyPage : public CPropertyPage
{
public:
static const COLORREF PROPERTYPAGE_BACKGROUND = RGB(68, 74, 80);
DECLARE_MESSAGE_MAP()
public:
CustomPropertyPage(UINT nIDTemplate);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
cpp
CustomPropertyPage::CustomPropertyPage(UINT nIDTemplate) : CPropertyPage(nIDTemplate)
{
}
BEGIN_MESSAGE_MAP(CustomPropertyPage, CPropertyPage)
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
HBRUSH CustomPropertyPage::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() != 0)
return CPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor);
HBRUSH hbr = CreateSolidBrush(PROPERTYPAGE_BACKGROUND_COLOR);
return hbr;
}
PropertySheet
Header
class CustomPropertySheet : public CPropertySheet
{
DECLARE_MESSAGE_MAP()
public:
CustomPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage);
virtual BOOL OnInitDialog();
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
private:
void Draw_Background(CDC *pDC);
};
cpp
CustomPropertySheet::CustomPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) : CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
}
BEGIN_MESSAGE_MAP(CustomPropertySheet, CPropertySheet)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
END_MESSAGE_MAP()
BOOL CustomPropertySheet::OnInitDialog()
{
BOOL answer = CPropertySheet::OnInitDialog();
CWnd* pTab = GetDlgItem(AFX_IDC_TAB_CONTROL);
SetWindowLongPtr(pTab->m_hWnd, GWL_STYLE, GetWindowLongPtr(pTab->m_hWnd, GWL_STYLE) | TCS_OWNERDRAWFIXED);
pTab->RedrawWindow();
return answer;
}
void CustomPropertySheet::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (nIDCtl == AFX_IDC_TAB_CONTROL)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
Draw_Background(pDC);
CRect rc(lpDrawItemStruct->rcItem);
rc.bottom += 1;
pDC->FillSolidRect(rc, CEasyPropertyPage::PROPERTYPAGE_BACKGROUND);
pDC->SetTextColor(GENERIC_TEXT_COLOR);
pDC->SetBkMode(TRANSPARENT);
char text[256];
TCITEM tci = { TCIF_TEXT | TCIF_STATE, 0, 0, text, 255, -1, 0 };
HWND tcw = ::GetDlgItem(m_hWnd, nIDCtl);
int i, tic = int(::SendMessage(tcw, TCM_GETITEMCOUNT, 0, 0));
for (i = 0; i < tic; ++i)
{
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
CRect tir;
::SendMessage(tcw, TCM_GETITEM, WPARAM(i), LPARAM(&tci));
::SendMessage(tcw, TCM_GETITEMRECT, WPARAM(i), LPARAM(&tir));
pDC->DrawText(text, tir, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
}
else CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
HBRUSH CustomPropertySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() != 0)
return CPropertySheet::OnCtlColor(pDC, pWnd, nCtlColor);
HBRUSH hbr = CreateSolidBrush(GENERIC_BACKGROUND_COLOR);
return hbr;
}
void CustomPropertySheet::Draw_Background(CDC* pDC)
{
CRect rect; this->GetClientRect(rect);
pDC->FillSolidRect(rect, GENERIC_BACKGROUND_COLOR);
rect.DeflateRect(0, 20, 0, 0);
pDC->FillSolidRect(rect, GENERIC_BORDER_COLOR);
}
To customize the light gray area (which is the embedded tab control) you need to override the OnDrawItem method in your class that is derived from CPropertySheet and do your custom drawing for the control with the AFX_IDC_TAB_CONTROL identifier. Something like this:
void MyPropertySheet::OnDrawItem(int nID, LPDRAWITEMSTRUCT pDIS)
{
if (nID == AFX_IDC_TAB_CONTROL) {
CDC* pDC = CDC::FromHandle(pDIS->hDC);
CRect rc(pDIS->rcItem); rc.bottom += 1;
pDC->FillSolidRect(rc, RGB(255, 0, 0)); // Or whatever b/g/ colour you want
pDC->SetTextColor(RGB(0,0,0)); // Or whatever text colour you want
pDC->SetBkMode(TRANSPARENT);
char text[256];
TCITEM tci = { TCIF_TEXT | TCIF_STATE, 0, 0, text, 255, -1, 0 };
CRect tir;
HWND tcw = ::GetDlgItem(m_hWnd, nID);
int i, tic = int(::SendMessage(tcw, TCM_GETITEMCOUNT, 0, 0));
for (i = 0; i < tic; ++i) {
::SendMessage(tcw, TCM_GETITEM, WPARAM(i), LPARAM(&tci));
::SendMessage(tcw, TCM_GETITEMRECT, WPARAM(i), LPARAM(&tir));
if (pDIS->itemState & ODS_SELECTED)
pDC->DrawText(text, tir, DT_CENTER |DT_VCENTER | DT_SINGLELINE);
}
pDC->Detach();
}
else { // Pass other stuff to the base class
CPropertySheet::OnDrawItem(nID, pDIS);
}
return;
}
Of course, be sure to add ON_WM_DRAWITEM() to the message map!
EDIT: You must also explicitly set the style of the embedded tab control to include TCS_OWNERDRAWFIXED. You can do this in the OnInitDialog override for your class.
EDIT 2: I have a slightly better way to get a pointer to the tab control, now! Also, I have added a few lines of code that act as a "cheat" to address the remaining area that needs to be coloured - by expanding the tabs to fit the width of the underlying control ...
BOOL MyPropertySheet::OnInitDialog()
{
BOOL answer = CPropertySheet::OnInitDialog(); // Call base class first!
// ... whatever other stuff you may wish to do
// CWnd* pTab = GetDlgItem(AFX_IDC_TAB_CONTROL);
CTabCtrl* pTab = GetTabControl(); // This is a bit clearer than above line!
// The following 4 lines comprise a 'first stab' at fixing the remaining issue:
CRect rcTab; pTab->GetWindowRect(&rcTab);
int nItems = pTab->GetItemCount();
int border = GetSystemMetrics(SM_CXEDGE) * 2;
pTab->SetMinTabWidth((rcTab.Width() - border) / nItems);
// ...
SetWindowLongPtr(pTab->m_hWnd, GWL_STYLE, GetWindowLongPtr(pTab->m_hWnd, GWL_STYLE) | TCS_OWNERDRAWFIXED);
pTab->RedrawWindow();
return answer;
}
Feel free to ask for further clarification and/or explanation.
I created a simple tab control that has 2 tabs (each tab is a different dialog). The thing is that i don't have any idea how to switch between tabs (when the user presses Titlu Tab1 to show the dialog i made for the first tab, and when it presses Titlu Tab2 to show my other dialog). I added a handler for changing items, but i don't know how should i acces some kind of index or child for tabs.
Tab1.h and Tab2.h are headers for dialogs that show only static texts with the name of the each tab.
There may be an obvious answer to my question, but i am a real newbie in c++ and MFC.
This is my header:
// CTabControlDlg.h : header file
//
#pragma once
#include "afxcmn.h"
#include "Tab1.h"
#include "Tab2.h"
// CCTabControlDlg dialog
class CCTabControlDlg : public CDialog
{
// Construction
public:
CCTabControlDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
enum { IDD = IDD_CTABCONTROL_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()
public:
CTabCtrl m_tabcontrol1;
CTab1 m_tab1;
CTab2 m_tab2;
afx_msg void OnTcnSelchangeTabcontrol(NMHDR *pNMHDR, LRESULT *pResult);
};
And this is the .cpp:
// CTabControlDlg.cpp : implementation file
//
#include "stdafx.h"
#include "CTabControl.h"
#include "CTabControlDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CCTabControlDlg dialog
CCTabControlDlg::CCTabControlDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCTabControlDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CCTabControlDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TABCONTROL, m_tabcontrol1);
}
BEGIN_MESSAGE_MAP(CCTabControlDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_NOTIFY(TCN_SELCHANGE, IDC_TABCONTROL, &CCTabControlDlg::OnTcnSelchangeTabcontrol)
END_MESSAGE_MAP()
// CCTabControlDlg message handlers
BOOL CCTabControlDlg::OnInitDialog()
{
CDialog::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)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
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
CTabCtrl* pTabCtrl = (CTabCtrl*)GetDlgItem(IDC_TABCONTROL);
m_tab1.Create(IDD_TAB1, pTabCtrl);
TCITEM item1;
item1.mask = TCIF_TEXT | TCIF_PARAM;
item1.lParam = (LPARAM)& m_tab1;
item1.pszText = _T("Titlu Tab1");
pTabCtrl->InsertItem(0, &item1);
//Pozitionarea dialogului
CRect rcItem;
pTabCtrl->GetItemRect(0, &rcItem);
m_tab1.SetWindowPos(NULL, rcItem.left, rcItem.bottom + 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
m_tab1.ShowWindow(SW_SHOW);
// al doilea tab
m_tab2.Create(IDD_TAB2, pTabCtrl);
TCITEM item2;
item2.mask = TCIF_TEXT | TCIF_PARAM;
item2.lParam = (LPARAM)& m_tab1;
item2.pszText = _T("Titlu Tab2");
pTabCtrl->InsertItem(0, &item2);
//Pozitionarea dialogului
//CRect rcItem;
pTabCtrl->GetItemRect(0, &rcItem);
m_tab2.SetWindowPos(NULL, rcItem.left, rcItem.bottom + 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
m_tab2.ShowWindow(SW_SHOW);
return TRUE; // return TRUE unless you set the focus to a control
}
void CCTabControlDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::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 CCTabControlDlg::OnPaint()
{
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
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CCTabControlDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CCTabControlDlg::OnTcnSelchangeTabcontrol(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
}
You can do this automatically in MFC by making the parent dialog a CPropertySheet and the contained dialogs CPropertyPage.
With the way you have it structured currently, you should do a ShowWindow for each of the dialogs with one set to SW_SHOW and the other to SW_HIDE in your OnTcnSelchangeTabcontrol function.
You should add your Tabcontrol click event and do the following codes. Dialog pointers pTab1 and pTab2 for each dialog/tab.
void CYourClass::OnNMClickTab1(NMHDR *pNMHDR, LRESULT *pResult)
{
pTab1->ShowWindow(SW_HIDE);
pTab2->ShowWindow(SW_HIDE);
for(int i=0;i<2;i++)
{
m_tbCtrl.HighlightItem(i,FALSE);
}
switch(m_tbCtrl.GetCurSel())
{
case 0: pTab1->ShowWindow(SW_SHOW);break;
case 1: pTab2->ShowWindow(SW_SHOW);break;
}
m_tbCtrl.HighlightItem(m_tbCtrl.GetCurSel(),TRUE);
*pResult = 0;
}