I am experimenting with painting the background of a window in c++ using the MFC library. It is mandated that i use this framework because I am working on an MFC application. I have tried several different methods but cannot get it to work. So i recently opened a blank project and just want to figure out how to paint the background but it is not working. Any help would be great. Here is my code...
class CExerciseApp : public CWinApp
{
//a pointer to our window class object
Basic_Window *bwnd;
BOOL InitInstance()
{
bwnd = new Basic_Window();
m_pMainWnd = bwnd;
bwnd->ShowWindow(1);
HWND hWnd = GetActiveWindow();
CRect drawing_area;
GetClientRect(hWnd, &drawing_area);
CBrush newBrush;
newBrush.CreateSolidBrush(RGB(255,255,255));
CDC* dc = bwnd->GetDC();
dc->FillRect(&drawing_area, &newBrush);
bwnd->RedrawWindow();
return TRUE;
}
};
From my own post https://stackoverflow.com/a/22875542/383779 , I can guarantee I had made that work. I used that approach for implementing themes/skins on a commercial application.
You need to add a OnCtlColor method to your Basic_Window class.
In your .h file, add to the Basic_Window class:
const CBrush m_BackgroundBrush;
and
afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor);
In .cpp file the constructor will initialize the new variable
Basic_Window::Basic_Window()
: m_BackgroundBrush(RGB(255,255,255))
{
//...
}
and implement
HBRUSH Basic_Window::OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if(some_exception)
return __super::OnCtlColor( pDC, pWnd, nCtlColor);
return (HBRUSH) m_BackgroundBrush.GetSafeHandle();
}
some_exception here means a situation where you will want the default behavior, instead of your own painting. Maybe it is a certain type of control, and for that exists the nCtlColor parameter.
Do not forget to add ON_WM_CTLCOLOR() to your message map.
Related
Tools : Visual Studio 2019, MFC, cpp
I'm looking for how to change the background color for the CFileDialog dialog box. I found this link ==> Q115087: HOWTO: Change the Background Color of a Common Dialog.
I have extracted this code and insert it all into my project then two files mydlg.h and mydlg.cpp. I replace the CFileDialog object with mydlg.
This the code included:
Header file ==> mydlg.h
//
#include <dlgs.h>
#define BACKGROUNG_COLOR RGB(0, 0, 255)
//////////////////////////////////////////////////////////////////////
// CMyDlg dialog
class CMyDlg : public CFileDialog
{
// Construction
public:
CMyDlg(CWnd* pParent = NULL); // standard constructor
// Add a CBrush pointer to store the new background brush
CBrush m_pBkBrush;
// Dialog Data
//{{AFX_DATA(CMyDlg)
enum { IDD = FILEOPENORD };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Generated message map functions
//{{AFX_MSG(CMyDlg)
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Code file CMyDlg.cpp
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/): CFileDialog(TRUE, NULL, NULL, OFN_HIDEREADONLY)
{
//{{AFX_DATA_INIT(CMyDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMyDlg, CFileDialog)
//{{AFX_MSG_MAP(CMyDlg)
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////////////
// CMyDlg message handlers
HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
m_pBkBrush.CreateSolidBrush(BACKGROUNG_COLOR);
switch (nCtlColor) { // ==> breakpoint here
case CTLCOLOR_STATIC:
{
// Set the static text to white on blue.
pDC->SetBkColor(BACKGROUNG_COLOR); return (m_pBkBrush);
}
case CTLCOLOR_DLG: return (m_pBkBrush);
default: return CFileDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
How i call it
CMyDlg FileOpenDialog(TRUE,NULL,local_File,OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY|OFN_PATHMUSTEXIST,
OpenFilter, // filter
AfxGetMainWnd()); // the parent window
CString local_string= Current_Dir();
FileOpenDialog.m_ofn.lpstrInitialDir = local_string;
status = Mess.LoadString(IDS_STRING191);
FileOpenDialog.m_ofn.lpstrTitle = Mess;
if (FileOpenDialog.DoModal() == IDOK)
{
pszSource = FileOpenDialog.m_ofn.lpstrFile;
return true;
}
return false;
Compilation OK
Observation the background color does not change
When i put a stop point on the switch in the function OnCtlColor we do not pass there.
Have you an idea, can you help me?
Thank you
Have you read this article How To Change the Background Color of a Common Dialog Q117778 - does not work where it says:
Changing the background colour of a standard File dialog seems to be possible but requires more steps than in case of a simple dialog.
If it is absolutely crucial to change the colour and no simpler solutions, then consider this summary:
Derive a class from CFileDialog.
In the constructor, get the value of m_ofn.lpfnHook and store to a variable. Write to m_ofn.lpfnHook an address of a new hook procedure. The new hook procedure will call the old one.
In the new hook procedure, intercept the WM_INITDIALOG message and do this: get the parent HWND and get the GWL_WNDPROC value (the old window procedure) of the parent and store it in a variable. Replace this procedure with a new window procedure. The new window procedure will call the old one.
In the new window procedure, intercept the WM_CTLCOLORDLG message and proceed as explained in documentation and above posts.
The linked conversation thread is dated 2009 which is more recent that your linked tutorial.
Guys, can someone give me a brief run through of how to change the background colour of a CEdit control at runtime? I want to be able to change the background to red if the field is zero length and the normal white otherwise.
You cannot do it with a plain CEdit, you need to override a few bits.
Implement your own ON_WM_CTLCOLOR_REFLECT handler, then return your coloured CBrush in the handler:
(roughly, you'll need to put the usual resource management in there, rememebr to delete your brush in the destructor)
class CColorEdit : public CEdit
{
....
CBrush m_brBkgnd;
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor)
{
m_brBkgnd.DeleteObject();
m_brBkgnd.CreateSolidBrush(nCtlColor);
}
}
This can also be done without deriving from CEdit:
Add ON_WM_CTLCOLOR() to your dialog's BEGIN_MESSAGE_MAP() code block.
Add OnCltColor() to your dialog class:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
Implement OnCtlColor() like so:
HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if ((CTLCOLOR_EDIT == nCtlColor) &&
(IDC_MY_EDIT == pWnd->GetDlgCtrlID()))
{
return m_brMyEditBk; //Create this brush in OnInitDialog() and destroy in destructor
}
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
Why do i get desktop as a parent HWND for my modal dialog here?
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
HWND parent = GetParent(); // or GetAncestor(m_hWnd, GA_PARENT);
assert(parent != GetDesktopWindow()); // not ok
...
}
....
}
//somewhere in code
//m_hWnd is some valid HWND
assert(m_hWnd != GetDesktopWindow()); //ok
CSaveProfileAsDlg dlg;
dlg.DoModal(m_hWnd /*as a parent wnd*/);
I can "solve" it by passing corret HWND in CSaveProfileAsDlg ctor, but i'd like to have correct solution.
Thank you!
The documentation is very confusing but I think I found the problem. DoModal internally calls ::DialogBox(), one parameter of which takes a HWND named hWndParent. From the documentation:
hWndParent [in, optional]
Type: HWND
A handle to the window that owns the dialog box.
The keyword here is the word "owns". The section about owned windows confirms this:
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
So we actually talk about the owner window instead of its parent. This makes sense as the dialog is a free floating window and not part of a window hierarchy as "parenthood" would imply.
You can get the owning window by using:
HWND parent = ::GetWindow(m_hWnd, GW_OWNER);
I had a similiar issue. I was wondering why GetParent() return always a different CWnd*.
The right solution is simple, just pass the desired pWnd to the dlg Constructor.
It will be stored in the CDialog member variable m_pParentWnd.
Then you can always get the passed pWnd with this member variable in your Dialog.
//somewhere in code
//pWnd some valid CWnd pointer
CSaveProfileAsDlg dlg (pWnd); // relevant!
dlg.DoModal();
.
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
CWnd* _pWnd = GetParent(); // not ok, returns probably CMainFrame or similiar
CWnd* pWnd = m_pParentWnd; // your parent Wnd
...
}
....
}
I've got two versions of the same application and one of them disables all parent popup windows as it should when DoModal is called. Second version disables only top-level CMainFrame and real parent is stay enabled so I can call modal dialog twice and more.
This happen in CWnd::GetSafeOwner_, line :
hWnd = ::GetLastActivePopup(hWnd);
Firsts version return real parent while second version return CMainFrame.
I've spent one day and could not find the cause of such behaviour. However I've found workaround:
When DoModal is called it Disable CMainFrame so I can disable it's children also:
afx_msg void OnEnable(BOOL bEnable);
ON_WM_ENABLE()
void CMainFrame::OnEnable(BOOL bEnable)
{
std::for_each(m_bars.begin(), m_bars.end(),
[=](const std::pair<EBar, BaseBar*>& bar)
{
bar.second->EnableWindow(bEnable);
});
}
Maybe the reason I can't find how to do this is because it isn't allowed. My brain is now confused and I can't solve this on my own. Here it is, simplified:
The problem is, change colours of dialogs in runtime.
#define NEW_COLOR 0x00C4FFFF
// the base class
class MyDialog : public CDialog
{
//...
COLORREF m_clrBkgnd;
virtual void RefreshColor();
}
void MyDialog::RefreshColor()
{
m_clrBkgnd = NEW_COLOR;
Invalidate();
RedrawWindow();
}
// the derived class
class CTest : public MyDialog
{
// no m_clrBkgnd
// no RefreshColor
}
// in main
CTest * m_pTestsDlg;
// here we decide to change the colours of all dialogs...
if (m_pTestsDlg != NULL)
{
m_pTestsDlg->RefreshColor();
}
It runs into RefreshColor nicely and changes the value of m_clrBkgnd, but the dialog does not change colour.
The point is, I have an awful lot of dialogs, and I want to do RefreshColor without changing the dialog code in any way. I have a feeling it must be possible to do this just by changing the base class. In my sample I just change the background colour, obviously there are buttons and stuff, but if I can get this to work I can extrapolate...
On the other hand it might just be plain impossible, and that's why I can't find the answer.
Some dialogs are very complicated and may have to have their own implementation after all.
EDIT
Here is where m_clrBkgnd is used:
HBRUSH MyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
switch (nCtlColor)
{
case CTLCOLOR_STATIC:
{
pDC->SetBkMode(OPAQUE);
pDC->SetTextColor(0,0,0);
pDC->SetBkColor(m_clrBkgnd);// to set the background color
return (HBRUSH) hbr;
break;
}
case CTLCOLOR_BTN:
{
pDC->SetBkMode(TRANSPARENT);
return HBRUSH(m_brushHollow);
break;
}
default:
return (HBRUSH) hbr;
}
}
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.