Is there a way to change the color of the background for a MDIParent windows in MFC (2005)?
I have tried intercepting ON_WM_CTLCOLOR AND ON_WM_ERASEBKGND but neither work. OnEraseBkgnd does work, but then it gets overwritten by the standard WM_CTL color.
Cheers
Create a class deriving CWnd (CClientWnd for example)
In your CWnd-derived class handle
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnPaint(void);
afx_msg void OnSize(UINT nType, int cx, int cy);
You need the following message map entries:
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_SIZE()
In OnEraseBkgnd just return TRUE, you'll do all of the work in OnPaint
In OnPaint, do whatever you like. To fill with a color, you could do
CBrush brush;
brush.CreateSolidBrush(COLORREF(RGB( 80, 160, 240 )));
CRect clientRect;
GetClientRect(clientRect);
CPaintDC dc(this);
dc.FillRect(clientRect, &brush);
In OnSize, call the base class, then invalidate to force a repaint:
CWnd::OnSize(nType, cx, cy);
Invalidate(FALSE);
In your mainframe, declare a member CClientWnd (m_clientWnd for example)
In your mainframe's OnCreate, first call the superclass, then
m_clientWnd.SubclassWindow(m_hWndMDIClient);
The CMDIFrameWnd is actually covered up by another window called the MDIClient window. Here is a Microsoft article on how to subclass this MDIClient window and change the background colour. I just tried it myself and it works great.
http://support.microsoft.com/kb/129471
There appears to be a simple solution to this problem in VS2008 at least:
In
CMainFrame::OnPaint()
{
..
// get the dc of the background window
CWnd* pWnd = CWnd::FromHandle(m_hWndMDIClient) ;
CPaintDC dc1(pWnd) ;
//and then draw or bitblt in this DC
}
Just guessing here, but try handling ON_WM_PAINT.
Related
By default the color is gray, I want to change it.I use OnEraseBkgnd in my MainFarm.h,this works, it changes color,but when somewhere further in the code mfc changes it to gray again.
BOOL CMainFrame::OnEraseBkgnd(CDC* pDC)
{
CBrush backBrush(RGB(0, 0, 0));
CBrush* pPrevBrush = pDC->SelectObject(&backBrush);
CRect rect;
pDC->GetClipBox(&rect);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),
PATCOPY);
pDC->SelectObject(backBrush);
return TRUE;
}
A MDI application doesn't just have frame windows and child windows. It also has a client window. The client window handles most of managing child windows.
But it also draws the client area of the frame window. This is what's drawing the grey background after you draw your background when you handle OnEraseBkgnd in the frame window.
Assuming your frame is derived from CMDIFrameWndEx, you should have a OnEraseMDIClientBackground virtual function that you can override to do the drawing you want.
If you're modifying older code, it's possible it uses an old enough version of MFC that's not present. If so, you need to create a window class and do the correct drawing in its onEraseBkgnd, create an instance of that in your frame window class, and in the frame window's onCreate, you subclass the MDI child window:
class MyBkgndErase : public CWnd {
public:
BOOL OnEraseBkgnd(CDC* pDC) {
// drawing code here
}
};
class MyFrameWnd : public CMDIFrameWnd {
MyBkgndErase eraser;
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
// there's probably existing code you'll want to preserve here
eraser.SubclassWindow(m_hWndMDIClient);
return 0;
}
};
Or, if you can switch to a newer version of MFC, you can probably just change your frame window's parent class from CMDIFrameWnd to CMDIFrameWndEx, and use OnEraseMDIClientBackground (which is undoubtedly easier).
I have a CStatic picture control on a CDialog that I use to draw content:
CMyDrawingControl.h
CMyDrawingControl : CStatic
{
//Constructor, Destructor, other items
//End Constructor, Destructor, other items
public:
void DrawStuff(CDC *dc);
protected:
afx_msg void OnPaint();
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
}
CMyDrawingControl.cpp
CMyDrawingControl::CMyDrawingControl
{
}
BEGIN_MESSAGE_MAP(CMyDrawingControl, CStatic)
//{{AFX_MSG_MAP(CMyDrawingControl)
ON_WM_VSCROLL()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMyDrawingControl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
//determine delta
ScrollWindow(0, -delta);
Invalidate();
UpdateWindow();
}
void CMyDrawingControl::OnPaint()
{
CPaint dc(this);
DrawStuff(&dc);
}
void CMyDrawingControl::DrawStuff(CDC *dc)
{
dc->SetMapMode(MM_LOMETRIC);
//draw on dc
//text, lines, shapes, etc
}
However the content is usually bigger than the control so I need to be able to scroll the content. CScrollView automatically handles by drawing to the view in OnDraw, but I can't seem to get it to work in OnPaint(). The control either will draw blank or has a lot of repeated content when scrolling.
I'm basically trying to duplicate the exact behavior of CScrollView on a CDialog; I've seen some posts that come close to this, but I don't want to implement a CDocument and a CView.
I think it is much simpler to use a read only edit control.
Beside scrolling this also makes it possible that the user can select and copy parts of the text.
i have to draw something in dialog box mfc , so , can i call a single document (because i know how to draw in single document MFC ) , from my main dialog box ?
thnx
You can use the same drawing code both in a CView and in a CDialog.
On the CView drawing works like this:
void CMyView::OnDraw(CDC* pdc)
{
// Draw something on pdc
Draw(pdc);
}
In a CDialog, override the OnPaint() handler, and draw like this:
void CMyDialog::OnPaint()
{
CPaintDC dc(this);
CPaintDC* pdc = &dc;
CDialog::OnPaint();
// Draw something on pdc
Draw(pdc);
}
Instead of a view in a dialog box, use a window derived from CStatic and make it to the size of dialog box on OnSize. It will be better than adding a view in a dialog box.
You have to override the OnPaint() event of the static control. To avoid flicker you have to override OnEraseBkgrnd() event.
class CMyStatic : public CStatic
{
public:
CMyStatic ()
~CMyStatic();
protected:
afx_msg void OnPaint();
void Draw(CDC *pDC);
};
void CMyStatic::Draw(CDC *pDC)
{
}
void CMyStatic::OnPaint()
{
CPaintDC dc(this);
Draw(&dc);
}
I created a custom control whose class has CStatic as base class. Currently I handle the drawing using WM_PAINT event. But there is a strange behavior. When I re-enable the window after disabling it using CWnd::EnableWindow function, it refuses to draw what I written in OnPaint function. It draws the static control instead.
I agree that there is this standard method of overriding DrawItem and using SS_OWNERDRAW style. But what's wrong with WM_PAINT?
void XXControl::OnPaint()
{
CPaintDC PaintDC( this );
// ** draw the control to PaintDC**
}
Here is exactly what I have written:
class CMyStatic : public CStatic
{
DECLARE_MESSAGE_MAP()
public:
void OnPaint(void);
};
BEGIN_MESSAGE_MAP(CMyStatic, CStatic)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyStatic::OnPaint(void)
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.FillSolidRect(&rect, RGB(120,255,0));
}
And subclassed:
class CMyDlg : public CDialog
{
// Construction
CMyStatic my_static;
...
};
BOOL CCMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
my_static.SubclassDlgItem(IDC_DRAW, this);
return true;
}
Where IDC_DRAW is static control on resource for this dialog. I wrote two button handlers:
void CMyDlg::OnBnClickedOk()
{
my_static.EnableWindow(FALSE);
my_static.Invalidate();
}
void CMyDlg::OnBnClickedOk2()
{
my_static.EnableWindow();
my_static.Invalidate();
}
And it works flawlessly! Remove Invalidate call and it would fail.
Try turning off Aero. I'm having a similiar problem where I'm drawing a static control and when it goes from disabled to enabled the WM_PAINT message is never received, but if I turn Aero off it works fine.
I'm building a project with MFC Feature Pack. Is this project I have a window which includes a CView, which includes a CListCtrl-derived object. The object includes the LVS_EDITLABELS flag.
Somehow I cannot edit the CListCtrl icon labels by two-time clicking (not double-clicking) on the icon label. After I select the item with a single click, a second click just flashes the item (button down turns text background to white, button up turns it back to blue) and the edit control never appears.
I reduced this problem to the simplest form, and even with a plain CListCtrl object I cannot edit the labels.
I also found that:
This problem occurs in VS2008. It doesn't occur in a similar project built in VS2003.
I am able to edit the labels if I build a CListView instead of a CView+CListCtrl.
I am also able to edit the labels if I build a CFormView and put the CListCtrl inside the resource dialog.
Here's some code in the simplest form: the .h file:
// vwTerminaisTeste.h
//
#pragma once
// vwTerminaisTeste view
class vwTerminaisTeste : public CView
{
DECLARE_DYNCREATE(vwTerminaisTeste)
protected:
vwTerminaisTeste(); // protected constructor used by dynamic creation
virtual ~vwTerminaisTeste();
CListCtrl m_lstTerminais;
protected:
DECLARE_MESSAGE_MAP()
virtual void OnDraw(CDC* /*pDC*/);
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
};
and the .cpp file:
// vwTerminaisTeste.cpp : implementation file
//
#include "stdafx.h"
#include "vwTerminaisTeste.h"
// vwTerminaisTeste
IMPLEMENT_DYNCREATE(vwTerminaisTeste, CView)
vwTerminaisTeste::vwTerminaisTeste()
{
}
vwTerminaisTeste::~vwTerminaisTeste()
{
}
BEGIN_MESSAGE_MAP(vwTerminaisTeste, CView)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
// vwTerminaisTeste message handlers
void vwTerminaisTeste::OnDraw(CDC* /*pDC*/)
{
CDocument* pDoc = GetDocument();
}
int vwTerminaisTeste::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_lstTerminais.Create(WS_CHILD | WS_VISIBLE | LVS_EDITLABELS, CRect(0,0,1,1), this, 0);
m_lstTerminais.InsertItem(0, "Teste", 0);
return 0;
}
void vwTerminaisTeste::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (IsWindow(m_lstTerminais.GetSafeHwnd()))
m_lstTerminais.MoveWindow(0, 0, cx, cy);
}
This way I cannot edit labels.
To change it to a CListView I simply replaced CView by CListView and m_lstTerminais by GetListCtrl(), and removed the OnCreate and OnSize implementations. That way it worked.
Note: the vwTerminaisTeste is created from a CSplitterWndEx within a CMDIChildWndEx-derived class.
Well nobody solved this problem but I managed to go around it by changing the CView to a CFormView and building a resource dialog with a ListView control, attaching it to the CListCtrl-derived class.
If anyone still has any suggestions on how could I solve this problem entirely, I'd appreciate them.
This sounds like it may be a focus or command routing issue, although that doesn't explain why it works OK in VS2003. You might try routing the command and/or focus messages from the splitter ctrl to vwTerminaisTeste, and/or from the MDIChild to the splitter. If you haven't already, you may need to derive your own splitter window. The command/focus forwarding would be something like...
BEGIN_MESSAGE_MAP(MySplitter, CSplitterWnd)
ON_WM_SETFOCUS()
END_MESSAGE_MAP(...)
void MySplitter::OnSetFocus(CWnd* pOldWnd)
{
// forward focus to the view window
m_vwTerminaisTeste.SetFocus();
}
BOOL MySplitter::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// let the view have first crack at the command
if (m_vwTerminaisTeste.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// otherwise, do default handling
return MySplitter::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}