Aus einer selbstgeschriebenen Klasse auf das Fenster "malen"
-
Vielleicht hat ja jemand Ahnung von der MFC, ich kann jedem nur empfehlen nie damit arbeiten zu müssen.
Ansich ein ganz banales Programm, ich will ein Spielrahmen aufmalen und darin soll man per Maus eine Box bewegen.
Den Spielrahmen gebe ich ohne Probleme aus:
#include <afxwin> #include "box.h" class CMyWnd: public CFrameWnd { public: CMyWnd() { Create(NULL, "RedBox Version 0.1", WS_SYSMENU,CRect(100, 100, 500, 600)); } protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) ON_WM_PAINT() END_MESSAGE_MAP() void CMyWnd::OnPaint() { CPaintDC dc(this); CPen pen(PS_SOLID, 3, RGB(0, 0, 0)); //Pinsel CBrush brush0(RGB(210, 210, 210)); //grau CBrush brush1(RGB(0, 0, 0)); //schwarz CBrush brush2(RGB(255, 255, 255)); //weiß CBrush *pOldTool = dc.SelectObject(&brush0); //Die Drei Rahmen werden gemalt dc.Rectangle(0, 0, 390, 464);//Rechteck und seine Position pOldTool = dc.SelectObject(&brush1); dc.Rectangle(10, 10, 380, 454); pOldTool = dc.SelectObject(&brush2); dc.Rectangle(20, 20, 370, 444); } class CMyApp: public CWinApp { public: virtual BOOL InitInstance(); }; BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyWnd; m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; }; CMyApp myApp;Nun möchte ich aber in meiner Klasse Box eine Funktion anzeigen() schreiben, die immer nachmdem der Timer bewegen() ausgeführt hat aufrufen soll um die neue Position anzuzeigen.
Nun das malen eines Rechtecks ist ja nicht das Problem, ich habe die Funktion so geschrieben:
void Box::anzeigen() { CPaintDC dc(???); // (1) hier weiß ich nicht weiter CBrush brush(RGB(m_rot, m_gelb, m_blau)); CBrush *pOldTool = dc.SelectObject(&brush); dc.Rectangle(m_pos_x, m_pos_y, m_breite, m_hoehe); }Das Problem hier ist nun das man an Stelle (1) ansich den Pointer auf den CWnd Typ angeben muss. In der OnPaint Funktion meines Fensters oben natürlich einfach der this-Pointer. Wie sage ich ihm nun aber außerhalb in meiner Klasse wo er das zeichnen soll? Habe schon probiert den this-Pointer an die anzeigen()-Funktion weiterzugeben. Aber ohne Erfolg.
Also wenn jemand ein Geistes-Blitz oder eine "Mach das doch viel einfacher so"-Idee hat. Würd ich mich freuen

Anmerkung: Ich schreib das ganze mit dem MS Visual Studio 6.0, der Assistent darf auch benutzt werden, jedoch wollt ich den erstmal außen vor halten um es nicht gleich zu unübersichtlich zu machen.
-
Virus73 schrieb:
Vielleicht hat ja jemand Ahnung von der MFC, ich kann jedem nur empfehlen nie damit arbeiten zu müssen.
Das ist doch schon wieder nur nachgeplappert. Viele schimpfen drauf, nur weil sie sich nie die Mühe machen, die Idee der MFC zu verstehen.
Aber zu deinem Problem. Ich geh mal davon aus, dass das eine dialogfeldbasierte Anwendung ist. Generell solltest du mal hier schauen, wie du einen Zeiger auf dein Viewobject bekommst. Das Zauberwort sollte hier AfxGetApp()->... heißen.Aber noch eine Frage: warum lagerst du das in eine eigene Klasse aus? Kann man zwar machen... aber man hat dann immer das Problem sich irgendwelche Zeiger herzucasten.
Wie und wo willst du denn deine Anzeigefunktion nutzen bzw. aufrufen?
Bedenke, dass alles was außerhalb der OnPaint gemalt wird nicht automatisch aktualisiert wird. Das soll heißen: wenn das Fenster bewegt oder überdeckt wird dann können nachträglich gezeichnete Objekte weg sein, da ja nur die OnPaint aufgerufen wird. Um das Zeichen deiner Objekte und den Refresh musst du dich dann jedesmal selber kümmern.
-
Danke schonmal Andy.
Ich bin auf Grund deines Arguments von der OnPaint Funktion möglichst wenig abzurücken nun dazu umgestiegen keine extra Klasse für die Boxen zu schreiben.
Ich habe es jetzt soweit das ich in meiner OnPaint Funktion den Rahmen Zeichne und auf OnMouseMove sich ein Rotes Rechteck mit der Maus bewegt.
Nun würd ich gern einen Timer so verbinden das bei jedem Timer-Flag drei andere blaue Kisten ihre Richtungen um einen Schritt fortsetzen. Es wird also ein Minispiel wo die rote Box den blauen ausweichen muss.
Ich habe es mir im Grunde nun so vorgestellt, das ich das rote Rechteck immer dann zeichne wenn es bewegt wird und die 3 blauen immer in der OnPaint male, und die OnPaint dann bei jeder Timer-Flag nochmal extra aufrufe.
Also in Sinnsprache etwa sowas:
WennTimerFunktion() { koordinaten von blaue box 1-3 ändern sich; OnPaint(blauebox1, blauebox2, blauebox3); //übergabe an OnPaint um die Boxen mit neuen Koordinaten neuzumalen }Das Problem hierbei ist nur, wo muss ich dann meine Boxen definieren damit die Koordinaten nicht verloren gehen? Es würd mir ja reichen sie global zu definieren damit ich überall darauf zugreifen kann.
-
Solche Daten global zu Halten ist nicht gut. In einer objektorientierten Umgebung muss man genau schauen, wer wann auf Daten zugreifen darf. Aus diesem Grund gibts ja bei der SDI-Anwendung die Doc/View-Trennung. Dort kann man die Daten auch besser kapseln und mittels Get- und Set-Funktionen nach außen hin erreichbar machen. Dieser Dokument-Ansicht-Trennung wäre das Paradebeispiel für dein Problem. In der Doc-Klasse hältst du die Daten und führst die Berechnungen durch. Dem View kannst du dann per Message sagen, dass es sich neu zeichnen soll. Damit hast du das Problem nicht. Bei der dialogfeldbasierten Anwendung gibts glaub ich vorab keine Möglichkeit einer Doc/View-Trennung. Vielleicht solltest du dir eine Klasse schreiben, die die Daten hält. Wenn du geometrsiche Firguren mit der Maus bewegen willst du dann andere Figuren noch ausweichen sollen, hast du mit der Kollisionskontrolle ohnehin genug Rechnerei. Bei jedem Mousemove musst du ja die Koordinaten einer jeden Geometrie neu berechnen. Danach brauchst du nur noch dem View sagen, dass es sich neu zeichnen soll. Den Rest übernimmt das Anwendungsgerüst. Und so was hier gibts schonmal gar nicht:
WennTimerFunktion() { koordinaten von blaue box 1-3 ändern sich; OnPaint(blauebox1, blauebox2, blauebox3); //übergabe an OnPaint um die Boxen mit neuen Koordinaten neuzumalen }Die OnPaint- oder beim SDI die OnDraw-Funktion werden nie direkt aufgerufen. Das macht immer da Anwendungsgerüst.
Na, immer noch keine Lust auf MFC?
Hier noch ein Tut: http://www.cpp-tutor.de
Dort findest du alles leicht verständlich erklärt.
-
Danke, dann werd ich versuchen das Projekt als SDI aufzuziehen.
Das Tutorial werd ich mir mal durchlesen vielleicht bin ich dannach ja großes MFC Fan
-
Ein sehr gut geschriebenes Tutorial. Das mit der Doc-/Viewtrennung klingt auch sehr gut für mein Fall.
Jetzt eine Frage zur SDI allgemein. Ist es möglich die Dialogfelder "Datei öffnen" etc komplett auszublenden? Habe im Assistenten alles andere Ausblenden können, aber das "Datei" und "Bearbeiten" Menü ist scheinbar fix.
Ich stell mir das so vor das man das Programm einfach startet und losspielt und beim Verlieren wird der Punktestand in einer Highscore gespeichert, also sind die Schaltflächen für meinen Fall überflüssig.
-
Virus73 schrieb:
Ein sehr gut geschriebenes Tutorial. Das mit der Doc-/Viewtrennung klingt auch sehr gut für mein Fall.
Jetzt eine Frage zur SDI allgemein. Ist es möglich die Dialogfelder "Datei öffnen" etc komplett auszublenden? Habe im Assistenten alles andere Ausblenden können, aber das "Datei" und "Bearbeiten" Menü ist scheinbar fix.
Ich stell mir das so vor das man das Programm einfach startet und losspielt und beim Verlieren wird der Punktestand in einer Highscore gespeichert, also sind die Schaltflächen für meinen Fall überflüssig.
Die Dialogfelder kannst Du nicht ausblenden, die stellt ja das Anwendungsgerüst zur Verfügung. Du kannst nur im Resourceneditor im Menü IDR_MAINFRAME die Einträge Datei und Bearbeiten einfach entfernen. Dann kommt zumindest keiner mehr dran. Ggf. müsstest Du dann auch die Shortcuts (Strg+N, ...+O, ...+S,....) abfangen, sonst kommt man zumindest darüber noch an die Dialogefelder ran.