How to paint an MFC element? - mfc

I'm not very good at MFC.
All i want to do is paint element (button, for example) when the button was pressed. I found this, but it only works when rendering dialog.
HBRUSH CSmartDeviceDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (nCtlColor==CTLCOLOR_STATIC)
{
return a;
}
if (nCtlColor==CTLCOLOR_EDIT){
return a;
}
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);;
}
Give me some advice how do I change it to smth like this:
void CSmartDeviceDlg::OnClick()
{
//some code to paint my elements
}
Thx for future responses and do not judge strictly =)

You should obviously place the button on the dialog somewhere physically. Code wise you should override the button onDraw to draw a custom button.
What you will achieve depends on what you want to do with the button, you can override as low as the actual drawing rectangle which represents the button and draw over it whatever you like.

You would need to invalidate the rectangle occupied by your elements to force windows to repaint them, like
CRect rcElement;
GetDlgItem(IDC_ELEMENT)->GetWindowRect(&rcElement); // in screen coordinates
ScreenToClient(&rcElement); // convert to client coordinates
InvalidateRect(&rcElement);
Note that this can cause "flickering" of the elements because the background will be erased before the elements are repainted.

Related

MFC: How do I change background color in MFC?

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).

Are there SuspendLayout() and ResumeLayout() functions or their equivalents that can be used in an MFC SDI application?

I want to suspend redrawing while I'm resizing the main window. I have handlers in CMainFrame for OnEnterSizeMove() and OnExitSizeMove(), and thought I might be able to use SuspendLayout() and ResumeLayout() there, but these functions apparently don't exist in CFrameWnd or CWnd.
void CMainFrame::OnEnterSizeMove()
{
// this->SuspendLayout(); (won't compile)
CFrameWnd::OnEnterSizeMove();
}
void CMainFrame::OnExitSizeMove()
{
// this->ResumeLayout(true); (won't compile)
CFrameWnd::OnExitSizeMove();
}
Thanks, SetRedraw() worked like a charm to stop redrawing. When I resize the window, redrawing stops, and when I stop resizing, the window redraws, BUT my custom toolbar doesn't redraw until I hover over each icon, when they reappear. Here's my code now:
void CMainFrame::OnEnterSizeMove()
{
SetRedraw(FALSE);
CFrameWnd::OnEnterSizeMove();
}
void CMainFrame::OnExitSizeMove()
{
SetRedraw(TRUE);
Invalidate();
CFrameWnd::OnExitSizeMove();
}

Can't detect mouse down on MFC Picture control

I am trying to add a picture control in a mfc dialog. I want to get the x,y coordinates of the pixel when I click on the picture control. I am using this code as a reference code and following this discussion to detect the click on the picture control.
A separate class has been derived for the picture control and PreTranslateMessage has been implemented.
BOOL CPictureCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if (pMsg->message == WM_LBUTTONDOWN && GetDlgItem(IDC_STATIC_BITMAP)->GetSafeHwnd() == pMsg->hwnd)
{
CPoint point(pMsg->pt);
ScreenToClient(&point);
OnLButtonDown(pMsg->wParam, point); //passes the coordinates of the clicked Point in the dialog box
}
return CStatic::PreTranslateMessage(pMsg);
}
In the main Dialog class I am calling the OnLButtonDown as follows
void CCPictureCtrlDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rect;
m_picCtrl.GetWindowRect(&rect);
ScreenToClient(&rect);
if (rect.PtInRect(point))
{
// Do something
}
CDialog::OnLButtonDown(nFlags, point);
}
PreTranslateMessage is only triggered when i hover the mouse over the picture control, it does not trigger when i click on the picture. and the OnLButtonDown of the main dialog is does not trigger when
I click on the picture control, it only triggers when I click anywhere other than the picture control.
Thanks.

CStatic does not invalidate every time its text is changed

I am trying to dynamically change the text of a CStatic control. My member variable is called mStatic of the type CStatic. I have changed the ID to IDC_MYSTATIC instead of IDC_STATIC.
I am calling mStatic.SetWindowText("asdfasdf") when I want to change the text of the control. I do this periodically in a timer.
Now I have the problem that the previous text is not erased after I call the SetWindowText(). It just keeps piling up until I get a mess on the screen.
The parent window has the layered property with a bitmap background. I have also set the color_key property so a certain color of the bitmap is viewed as transparent (I.e. It will not be drawn and will let mouse messages through). The mStatic control is drawn on the parts not transparent, that have a bitmap background.
Why isn't the window invalidating?
Had the same issue. The following code fixed it:
mStatic.SetWindowText("New text");
CRect rect;
mStatic.GetWindowRect(&rect);
ScreenToClient(&rect);
InvalidateRect(&rect);
UpdateWindow();
Perhaps your static text control have a SS_SIMPLE style enabled. You can check style flags on resource file or using GetStyle().
Static control with SS_SIMPLE style displays text faster, but also - as MSDN describes -
"SS_SIMPLE static controls do not clear the control's display area when displaying text. If a shorter string is displayed, the part of the original string that is longer than the new shorter string is displayed."
Clear SS_SIMPLE from style flags and CStatic will behave 'normally'.
This knowledge base support article describes the same problem when the SetWindowText() call is made from another thread. Is that what your timer is doing?
If so the solution could simply be to:
mStatic.SetWindowText("asdfasdf");
CRect clientRect;
mStatic.GetClientRect(clientRect);
mStatic.InvalidateRect(clientRect);
As mentioned by others already, a static control doesn't necessarily erase its background prior to drawing the text.
I find it a much better solution to subclass the static control and force the invalidation of the control from there. This enables one to easily implement it on all static texts with transparent background, without having to do extra calls to invalidate the control from its parent class.
One way to catch a change of the control's text from within the control itself is to react to the WM_SETTEXT message and force the invalidation from there:
int CStaticT::OnSetText(LPCTSTR text)
{
LRESULT res = Default();
Invalidate();
UpdateWindow();
return res;
}
Here is a brief example, extracted from one of my classes, of how such a subclassed control could look like:
//////////////////////////////////////////////////////////////////////////
// Header
//////////////////////////////////////////////////////////////////////////
class CStaticT : public CStatic
{
DECLARE_DYNAMIC(CStaticT)
public:
CStaticT();
virtual ~CStaticT();
protected:
afx_msg int OnSetText(LPCTSTR text);
DECLARE_MESSAGE_MAP()
private:
BOOL m_InitialSet;
};
//////////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNAMIC(CStaticT, CStatic)
CStaticT::CStaticT()
{
m_InitialSet = FALSE;
}
CStaticT::~CStaticT()
{
}
BEGIN_MESSAGE_MAP(CStaticT, CStatic)
ON_WM_SETTEXT()
END_MESSAGE_MAP()
int CStaticT::OnSetText(LPCTSTR text)
{
LRESULT res = Default();
// I've noticed issues when this forces the invalidation
// of the static control before the parent's background
// is painted completely.
// This is a cheap workaround, skipping the initial setting
// of the text by the subclassing call.
// You have to test if this works out for your environment.
if (!m_InitialSet)
{
m_InitialSet = TRUE;
return res;
}
// Force of the invalidation
Invalidate();
UpdateWindow();
return res;
}

Is there any function that removes icon, using visual studio 6.0?

I couldn't find any function which removes a loaded icon. Please help me:))
Or is there any other function which makes an icon hidden such as setVisible(false), or delete?
if(GetDlgItem (IDC_BUTTON1)->IsWindowEnabled())
{
m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
dc.DrawIcon(DT_CENTER,DT_CENTER, m_hIcon);
}
if(GetDlgItem (IDC_BUTTON1)->IsWindowEnabled()==0) //disabled
{ dc.SetTextColor(RGB(192,192,192));
m_hIcon = AfxGetApp()->RemoveIcon(IDI_ICON1);//HERE! // I try another icon
//which is null, however it didn't work because it is transparent.
}
Instead of painting another icon when the button is disabled, either draw a rectangle using a null pen and a brush of the background colour or just invalidate the icon-rectangle with bErase as TRUE when you disable the button
This code is a bit strange, especially since you don't give us any context. What is dc, a device context? Is this code inside of a OnPaint message handler function? And either way, why are you drawing the icon manually instead of letting the button control do it automatically?
The simpler approach is to monitor the enabled state of the control by listening for WM_ENABLE messages via the OnEnable member function, and calling the SetIcon member function to update the button's icon. This way, the button control keeps track of the icon, so there's no need to keep it in a member variable (m_hIcon). And removing an icon is as simple as setting it to display a null icon. There's no need or all of these ugly hacks, like using a second transparent icon, or drawing over the icon with a solid color rectangle.
Sample code, where CMyButton extends CButton:
void CMyButton::OnEnable(BOOL bEnable)
{
CButton::OnEnable(bEnable); // call the base class
if (bEnable)
{
// The button was enabled, so load and set the icon.
const HICON hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
this->SetIcon(hIcon);
}
else
{
// The button was disabled.
// First, retrieve the current icon from the button.
const HICON hIcon = this->GetIcon();
// Then, remove the icon from the button by setting a null icon.
this->SetIcon(NULL);
// Finally, delete the original icon to prevent memory leaks.
::DestroyIcon(hIcon);
}
}
It is, of course, possible to do it your way inside of the OnPaint function, if you absolutely must (though this is poor design). The trick is that the icon only gets drawn if the code to draw it gets executed. Otherwise, no icon gets drawn. The base class will not draw an icon.
So all you need is:
void CMyWindow::OnPaint()
{
CPaintDC dc(this);
const CWnd* pBtn = GetDlgItem(IDC_BUTTON1);
if (pBtn->IsWindowEnabled())
{
m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
dc.DrawIcon(DT_CENTER, DT_CENTER, m_hIcon);
// make sure that you delete m_hIcon when the window is destroyed!
}
else
{
// Do not draw the icon here!
// But write whatever other code you want, like...
dc.SetTextColor(RGB(192,192,192));
}
}
Obviously this tactic will only work if your code is inside of the OnPaint function, but that is where you should do all of your drawing. Otherwise it will be lost whenever the window is repainted. For a demonstration, just try minimizing the window or moving it off the screen. Force a repaint by calling CWnd::Invalidate, followed by CWnd::UpdateWindow (or alternatively `CWnd::RedrawWindow).