I'm a little new to using MFC and VC++ as such, but I'm doing this as part of a Course and i Have to stick to VC++.
http://www.cprogramming.com/tutorial/game_programming/same_game_part1.html
This is the tutorial I have been following to make a simple samegame. However when i try to display score, the score is getting displayed Underneath or outside my application window, even though I've displayed score before calling updateWindow(). I've tried various methods but I am kinda lost here.
Here is the code I'm using to Display the score:
void CSameGameView::updateScore()
{
CSameGameDoc* pDoc = GetDocument();
CRect rcClient, rcWindow;
GetClientRect(&rcClient);
GetParentFrame()->GetWindowRect(&rcWindow);
int nHeightDiff = rcWindow.Height() - rcClient.Height();
rcScore.top=rcWindow.top + pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
rcScore.left=rcWindow.left + 50;
rcScore.right=rcWindow.left + pDoc->GetWidth() - 50;
rcScore.bottom=rcScore.top + 20;
CString str;
double points = Score::getScore();
str.Format(_T("Score: %0.2f"), points);
HDC hDC=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);
COLORREF clr = pDoc->GetBoardSpace(-1, -1); //this return background colour
pDC->FillSolidRect(&rcScore, clr);
DrawText(hDC, (LPCTSTR) str, -1, (LPRECT) &rcScore, DT_CENTER);
}
Thank you for any help and I'm sorry if the question doesn't make sense or in ambiguous.
There are several problems with your code:
1. The hDC you are creating is going to have coordinates relative to the desktop window. To paint text in your window, use CClientDC like this: CClientDC dc(this); (see http://msdn.microsoft.com/en-US/library/s8kx4w44%28v=vs.80%29.aspx)
2. The code you have will leak a DC every time the function is called. The method in #1 will fix that.
3. Your paint code should be done in the CView::OnDraw. There you get a DC passed to you and you don't have to worry about creating one with CClientDC. Set the variables you want to draw (e.g. your points or score), store them as class members and draw them in CView::OnDraw.
Don't do the drawing in your updateScore method.
Make sense? Hang in there!
Related
It has been 10 months since I worked on my app due to a death in the family, just started looking at it again and still not sure how to solve the problem.
The project inquires/help started here:
MFC MDI Collecting control states for the "apply" button routine
Since this is a specific focused question, I didn't want to muck up my other thread, so what I'd like to do is change the documents tab styles after the view is loaded. I know that this can be done because the master repository from Microsoft with all the code examples has a project called VCSamples-master\VCSamples-master\VC2010Samples\MFC\Visual C++ 2008 Feature Pack\TabControl which I have looked at. It dawns on me that even though I can follow its code, the calls are from within the MDI window itself where my issue is I'm trying to do this via a property page dialog using OnApply which changes things.
I was able to do part of this properly with the help of the thread above to the OutputPane successfully because I was able to get the Pane handle and execute. I was told that for the MDI tabs after creation that I need to parse the tabs, count them, and then execute. So my issue here is after I capture the tabs......how to change their styles.
Here is the code as it stands:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles (Works 100%)
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
//Get the open file tabs in the MDI
for (POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); pos != NULL; )
{
CDocTemplate* pTempl = AfxGetApp()->GetNextDocTemplate(pos);
for (POSITION pos1 = pTempl->GetFirstDocPosition(); pos1 != NULL; )
{
CDocument* pDoc = pTempl->GetNextDoc(pos1);
for (POSITION pos2 = pDoc->GetFirstViewPosition(); pos2 != NULL; )
{
CView* pView = pDoc->GetNextView(pos2);
if (pView->IsKindOf(RUNTIME_CLASS(CTrainView)))
{
// THIS IS WHERE MY ISSUE IS, NOW THAT ALL THE TABS ARE
// CAPTURED, HOW DO I ADDRESS THEM LIKE WHAT IS SHOWN
// ABOVE:
//((CMainFrame*)AfxGetMainWnd())->xxxxxx.yyyyyy.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
}
}
}
}
return bResult;
}
If I can figure this last piece out, I'll be basically finished, I just can't seem to find a solution on how to do this via property sheet via OnApply.
Any suggestions or actual code examples I can see to solve my problem?
FYI: No, I haven't had any time to take additional OOP to solve this. I'm hoping someone can provide some guidance so I can move on after getting this sorted.
Thanks,
Chris
EDIT 1:
So I took a closer look at Constantine's suggestion and here is what I came up with:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CMFCTabCtrl& MDI_STYLES = ((CMainFrame*)AfxGetMainWnd())->GetMDITabs();
MDI_STYLES.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
MDI_STYLES.RecalcLayout();
CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame());
pMainFrame->SetFocus();
pMainFrame->RecalcLayout();
}
return bResult;
}
The m_styles_tabs is getting the index value of 0-8 when I select the radio button. The code compiles and runs and I see the index value change when I break on it, but the tabs for the MDI are still not updating. Does the edited code make sense based on the members shown here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/cmfctabctrl-class?view=msvc-170#modifytabstyle
I think this the right direction, am I missing something?
There are a number of questions and answers about setting wallpapers programmatically on multi-monitor setups in Windows, but I'm asking specifically for Windows 10 (and maybe Windows 8) because it seems to work differently from all the explanations I found.
Raymond Chen has an article "How do I put a different wallpaper on each monitor?" (https://devblogs.microsoft.com/oldnewthing/?p=25003), also quoted in Monitors position on Windows wallpaper. The core concepts is that Windows places the top-left corner of the provided bitmap at the top-left corner of the primary monitor, and wraps around to fill any desktop space to the left and/or above that. I understand that, I wrote a little program using that knowledge, and it works beautifully in Windows 7.
How it works: I create a bitmap that conceptually covers the whole desktop space, as the user sees it. I draw the contents of each monitor to that bitmap in its appropriate position (the program is written in C++ using VCL, but the principle remains the same in other programming environments):
TRect GetMonitorRect_WallpaperCoords(int MonitorNum)
{
Forms::TMonitor *PrimaryMonitor = Screen->Monitors[0];
Forms::TMonitor *Monitor = Screen->Monitors[MonitorNum];
// Get the rectangle in desktop coordinates
TRect Rect(Monitor->Left, Monitor->Top, Monitor->Left + Monitor->Width, Monitor->Top + Monitor->Height);
// Convert to wallpaper coordinates
Rect.Left += PrimaryMonitor->Left - Screen->DesktopLeft;
Rect.Top += PrimaryMonitor->Top - Screen->DesktopTop;
Rect.Right += PrimaryMonitor->Left - Screen->DesktopLeft;
Rect.Bottom += PrimaryMonitor->Top - Screen->DesktopTop;
return Rect;
}
std::unique_ptr<Graphics::TBitmap> CreateWallpaperBitmap_WallpaperCoords()
{
std::unique_ptr<Graphics::TBitmap> Bmp(new Graphics::TBitmap);
Bmp->PixelFormat = pf24bit;
Bmp->Width = Screen->DesktopWidth;
Bmp->Height = Screen->DesktopHeight;
// Draw background (not that we really need it: it will never be visible)
Bmp->Canvas->Brush->Style = bsSolid;
Bmp->Canvas->Brush->Color = clBlack;
Bmp->Canvas->FillRect(TRect(0, 0, Bmp->Width, Bmp->Height));
for (int MonitorNum = 0; MonitorNum < Screen->MonitorCount; ++MonitorNum)
{
TDrawContext DC(Bmp->Canvas, GetMonitorRect_WallpaperCoords(MonitorNum));
DrawMonitor(DC);
}
return Bmp;
}
(The draw context uses a coordinate translation rect so that the code int DrawMonitor function can draw in a rectangle like (0, 0, 1920, 1080) without having to wonder where in the full bitmap it is drawing, and with a clip rect so that DrawMonitor can not accidentally draw outside of the monitor it's drawing on).
Then I convert that bitmap to an image that will properly wrap around when placed at the top-left corner of the primary monitor (as Raymond Chen describes in his article):
std::unique_ptr<Graphics::TBitmap> ConvertWallpaperToDesktopCoords(std::unique_ptr<Graphics::TBitmap> &Bmp_WallpaperCoords)
{
std::unique_ptr<Graphics::TBitmap> Bmp_DesktopCoords(new Graphics::TBitmap);
Bmp_DesktopCoords->PixelFormat = Bmp_WallpaperCoords->PixelFormat;
Bmp_DesktopCoords->Width = Bmp_WallpaperCoords->Width;
Bmp_DesktopCoords->Height = Bmp_WallpaperCoords->Height;
// Draw Bmp_WallpaperCoords to Bmp_DesktopCoords at four different places to account for all
// possible ways Windows wraps the wallpaper around the left and bottom edges of the desktop
// space
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft, Screen->DesktopTop, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft + Screen->DesktopWidth, Screen->DesktopTop, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft, Screen->DesktopTop + Screen->DesktopHeight, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft + Screen->DesktopWidth, Screen->DesktopTop + Screen->DesktopHeight, Bmp_WallpaperCoords.get());
return Bmp_DesktopCoords;
}
Then I install that bitmap as a wallpaper by writing the appropriate values in the registry and calling SystemParametersInfo with SPI_SETDESKWALLPAPER:
void InstallWallpaper(const String &Fn)
{
// Install wallpaper:
// There are 3 name/data pairs that have an effect on the desktop wallpaper, all under HKCU\Control Panel\Desktop:
// - Wallpaper (REG_SZ): file path and name of wallpaper
// - WallpaperStyle (REG_SZ):
// . 0: Centered
// . 1: Tiled
// . 2: Stretched
// - TileWallpaper (REG_SZ):
// . 0: Don't tile
// . 1: Tile
// We don't use the Wallpaper value itself; instead we use SystemParametersInfo to set the wallpaper.
// The file name needs to be absolute!
assert(Ioutils::TPath::IsPathRooted(Fn));
std::unique_ptr<TRegistry> Reg(new TRegistry);
Reg->RootKey = HKEY_CURRENT_USER;
if (Reg->OpenKey(L"Control Panel\\Desktop", false))
{
Reg->WriteString(L"WallpaperStyle", L"1");
Reg->WriteString(L"TileWallpaper", L"1");
Reg->CloseKey();
}
SystemParametersInfoW(SPI_SETDESKWALLPAPER, 1, Fn.c_str(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
But when I test it in Windows 10, it doesn't work properly anymore: Windows 10 puts the wallpaper completely in the wrong place. Seeing as other people have asked questions about multi-monitor wallpapers in the past, I'm hoping there are people with experience of it on Windows 10.
As far as I can see, Windows 10 places the top-left corner of the provided bitmap at the top-left corner of the desktop space (by which I mean the bounding rectangle of all monitors), instead of the top-left corner of the primary monitor. In code, that means: I leave out the ConvertWallpaperToDesktopCoords step, and then it works fine as far as I can see.
But I can't find any documentation on this, so I don't know if this is officially explanation of how Windows 10 does it. Use with care. Also I don't know when this different behavior started: in Windows 10, or maybe earlier in Windows 8.
I am developing MFC based SDI application using CFormView class in VC++. My problem is that I need to load image when the dialog initially appears. How to place image in an SDI application..I know for dialog based applications it can be done using OnInitDialog application.But for SDI application there is no such function. I tried placing the image using OnInitialUpdate() and OnPaint() function. But it failed..What should I do to place the image to dialog when it first appears? Please Help
Thanks in advance
Code as I placed in OnInitialUpdate()
void CECUSimulatorView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
hBitmap = LoadImage(0,_T("F:/ECUSimulator/ECUSimulator_New/res/LedOff.bmp"), IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
ImageLoading();
}
Code for the function ImageLoading()
void CECUSimulatorView::OnInitialUpdate()
{
HDC hDC, hDCToDisplay = NULL;
hDC = CreateCompatibleDC(hDCToDisplay);
SelectObject(hDC,hBitmap);
hDCToDisplay = ::GetDC(m_picture.m_hWnd);
m_picture.GetWindowRect(&picRect);
BitBlt(hDCToDisplay,0 , 0, (picRect.right - picRect.left), (picRect.bottom -picRect.top), hDC, 0 ,0 ,SRCCOPY);
DeleteDC(hDC);
DeleteDC(hDCToDisplay);
}
Here
HANDLE hBitmap;
CStatic m_picture; //Picture Control
CRect picRect; //Picture Control Rect
I removed the code from OnInitialUpdate() and placed it in OnPaint() function as follows:
void CECUSimulatorView::OnPaint()
{
CPaintDC dc(this); // device context for painting
hBitmap = LoadImage(0,_T("F:/ECUSimulator/ECUSimulator_New/res/LedOff.bmp"), IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
ImageLoading();
}
Calling LoadImage() in OnInitialUpdate() is ok, the actual painting needs to be done in one of the two following ways:
a) in CECUSimulatorView::OnDraw() -- easier, but may introduce flickering
b) override the OnPaint() function of the m_picture control using ClassWizard and draw the picture there
[the comments in the previous answer was getting very extended, hence a new answer]
a) derive a new class (say CMyPicture) based on CStatic, change m_picture to this new class
b) in CMyPicture, create a handler for WM_PAINT, this will normally be called CMyPicture::OnPaint()
c) change your ImageLoading() function to take a CDC *pDC as parameter and use this DC to render the image
d)
void CMyStatic::OnPaint(void)
{ CPaintDC dc(this);
ImageLoading(&dc); // or even move the painting logic here
}
NOTE: you can load the image in OnInitialUpdate(), no need to load it each time you are painting
An example (for a dialog, but the painting logic should not be affected) in this SO Answer.
I found the answer for my problem.
I used CBitmap class as follows:
CBitmap m_bBitmap1;
In OnInitialUpdate() I wrote as follows :
m_bBitmap1.LoadBitmapW(IDB_BITMAP1);
In OnPaint() I wrote as follows:
m_picture.SetBitmap(m_bBitmap1);
Wherever(In which all functions)need to load image just call the above line of code in corresponding functions..
I am trying to make a paint application in MFC using visul basic c++ 6.0 i have already created a window using Create function and also have created a toolbar with a tool line but i am stuck on how to code for the line because the function i know goes like d.lineTo(x,y) and d.Moveto(x2,y2) but it comes under the line function how do i use OnLButtonDown to Trap the co-ordiantes or is there any other way i can draw a line ..? any help will be useful
have a look at the MFC Scribble tutorial :
http://msdn.microsoft.com/en-us/library/aa716527%28v=vs.60%29.aspx)
It will get you started on how to handling mouse click and mouse move and drawing.
M.
Ok, you're going to have to override several member functions to do this. I've outlined an approach below. My example below deals with a single line-drawing operation (from mouse down, to mouse up). An exercise for you, is to make it so that once you've done one, you can then do another at a different place. It's easy, btw!
CWnd::OnLButtonDown(UINT _flags, CPoint _pt);
CWnd::OnLButtonUp(UINT _flags, CPoint _pt);
CWnd::OnMouseMove(UINT _flags, CPoint _pt);
CWnd::OnPaint()
Apologies if some of these function signatures are wrong! Add some members to your window class:
// at the top of your file
#include <vector>
// in your class
typedef std::vector<POINT> PointVector;
PointVector m_Points;
CYourWnd::OnLButtonDown(UINT _flags, CPoint _pt);
{
// NOTE: For more than one set of drawing, this will be different!
m_Points.clear();
m_Points.push_back(POINT(_pt.x, _pt.y));
}
CYourWnd::OnMouseMove(UINT _flags, CPoint _pt);
{
if(_flags & MK_LBUTTON)
{
const POINT& last(m_Points.back());
if(_pt.x != last.x || _pt.y != last.y)
{
m_Points.push_back(POINT(_pt.x, _pt.y));
Invalidate();
}
}
}
CYourWnd::OnPaint()
{
CPaintDC dc(this);
CRect rcClient; GetClientRect(&rc);
FillSolidRect(&rcClient, RGB(255, 255, 255));
if(m_Points.size())
{
dc.MoveTo(m_Points[0].x, m_Points[0].y);
for(PointsVector::size_type p(1);
p < m_Points.size();
++p)
dc.LineTo(m_Points[p].x, m_Points[p].y);
}
}
Obviously, this is crude and gives you a single drawing operation. Once you click the left button down again, it erases what you've done. So, once you have this working:
Make it so you can draw an unlimited amount of lines. You could accomplish this in several ways such as an additional container (to store vectors), or even drawing-operation classes that you can store in a single vector and then execute.
This solution may well flicker. How might you stop this? Perhaps OnEraseBkgnd holds the clue...
How about more colours?
All signs point towards creating some drawing-classes that encapsulate this for you, but I hope this has got you started.
I have a dialog that I want to place within another dialog and position relative to one of the controls on the main dialog.
void CspAceDlg::DrawResultsArea()
{
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
CRect rectTabCtl; // Allocate CRect for control's position.
pTabCtl->GetWindowRect(&rectTabCtl);
int resX = rectTabCtl.right + 15;
int resY = rectTabCtl.top;
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.ShowWindow(SW_SHOW);
}
My problem is that my dialog resource (IDD_REULTSDIALOG) has properties called X Pos and Y Pos that seem to be overriding my SetWindowPos() (and the little property tab in the resource editor won't let me leave these blank). If I set these properties to 0, 0 my dialog appears in the top left corner of the main dialog. If I set them to a number I can guess-and-test place it roughly where I want, but then running the application on different resolutions causes the dialog to appear in different spots. What I really want to do anyway is place the dialog relative to another control on my main dialog (in this case my tab control). Why is my SetWindowPos() being ignored, and how do I fix this? Should I be using a different function?
According to the documentation for SetWindowPos, if you pass in SWP_SHOWWINDOW, the window will not be moved:
If the SWP_SHOWWINDOW or SWP_HIDEWINDOW flag is set, the window cannot be moved or sized.
Figured it out myself, largely due to this thread.
My code came out looking like this:
void CspAceDlg::DrawResultsArea()
{
CRect rectTabCtl; // CRect representing tab control's position.
POINT pResXY;
POINT pResWH;
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
pTabCtl->GetWindowRect(&rectTabCtl);
pResXY.x = rectTabCtl.right + 15;
pResXY.y = rectTabCtl.top;
pResWH.x = pResXY.x + 175;
pResWH.y = pResXY.y + 135;
ScreenToClient(&pResXY);
ScreenToClient(&pResWH);
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
//results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.MoveWindow(pResXY.x, pResXY.y, pResWH.x, pResWH.y, TRUE);
results.ShowWindow(SW_SHOW);
}
What fixed this problem for me, was setting the program's compatibility properties to "run this program as an administrator".