Geometrische Figuren mit der Maus verschieben
-
Servus,
ich habe ein MFC Programm geschrieben (STI - Anwendung) bei dem mehrer vordefinierte geometrische Objekte (Dreieck, Rechteck, Polygon, Kreis). Auf Knopfdruck an vordefinierten koordinaten gezeichnet werden.Nun soll man diese aber per Mausklick in der Ebene verschieben können.
Ich frage da nur wie man das ganze umsetzten kann?Man müßte ja dann beim verschieben neu Zeichen (flackern ist nicht schlimm).
Aber woher weis das Programm dann wo die neuen Koordinaten der Objekte liegen?Achja ich habe den Anwendungsassistenten von Visual Studio 6 benutzt...
-
Hallo Dragonslayer!
Beim setzen deiner Figuren musst du das Programm alle relevanten Informationen zu Position und Größe über die Figur speichern.
Dann baust du Behandlungsroutinen für Mausevents ein (WM_LBUTTONDOWN).
Wenn jetzt die linke Maustaste gedrückt wird, dann musst du berechnen, über welcher Figur die Taste gedrückt wurde und dann entsprechend beim ziehen, die gespeicherten Informationen ändern.In deiner OnPaint zeichnest du jetzt die Figuren, anhand der gespeicherten Informationen. Das Thema Flackern ist halb so wild. Du musst eigentlich nur alle Zeichen-Operationen auf einem Hintergrund-DC ausführen und dieses dann mit einem Ruck auf den Bildschirm zeichnen. Dann flackert das Ding nicht und du kriegst kein Augenkrebs

Ich hoffe, dass ich dir helfen konnte.
Grüße,
Michael
-
ja ich denke das hilft mir weiterm ich hab bereits für ne andere anwendung in dem programm ( per Lbuutondown zeichenreihenfolge von 2 objekten ändern), für jedes Objekt eine Methode geschrieben mit der man testen kann ob man in ein Objekt geklickt hat.
Sieht beim rechteck zb so aus:
int RECHTECK::objekt_test_zeichnen(CPoint point) { CRgn region; region.CreateRectRgn( (int)(getX_pos()-(breite/2)), //x1 Specifies the x-coordinate of the upper-left corner. (int)(getY_pos()-(hoehe/2)), //y1 Specifies the y-coordinate of the upper-left corner. (int)(getX_pos()+(breite/2)), //x2 Specifies the x-coordinate of the lower-right corner. (int)(getY_pos()+(hoehe/2)) //y2 Specifies the y-coordinate of the lower-right corner. ); if(region.PtInRegion(point)) { region.DeleteObject(); return 1; } else { return 0; } return 0; }was mir aber noch nicht ganz klar ist, wie kommt man den an die neuen x/y daten auf die der mauszeiger geklickt hat?
-
void DEINPROG::OnLButtonDown(UINT nFlags, CPoint point)
point enthält die Klick-Koordinaten der Maus.
-
void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { if(fvec[n]->objekt_test_zeichnen(point)) { //Cursor hat innerhalb einer Objektfläche geklickt oberstes_objekt = n; fvec[n] = new DREIECK(100,100,75,point.x,point.y,obj_fuell_farbe); CPaintDC dc(this); fvec[n]->objekt_zeichnen(&dc); }ok ich habe das jetzt erstmal für ein dreieck gemacht, allerdings funktioniert das jetzt nur wenn ich inner halb des dreiecks woanders hinklicke, wie bekommt man den jetzt eine Abfrage das nur neu gezeichnet wird wenn die maus nicht nur geklickt sondern auch bewegt wird?
}
-
Ok ich hab jetzt auch noch ne Abfrage für Mausbewegung gemacht.
Nun habe ich noch 2 Probleme bei meiner aktuellen Umsetzung:
1.Verschiebe ich ein Objekt auf ein anderes so wird das darunterliegende Objekt gelöscht.
2. Ich weis nicht wie man eine fallunterscheidung bekommt, welches Objekt gerade angeklickt wurde und verschoben wird.
Also hier mal die wichtigsten Methoden:
void CChildView::OnPaint() { CPaintDC dc(this); // Gerätekontext zum Zeichnen for(int n=0; n< anz_objekte;n++) { fvec[n]->objekt_zeichnen(&dc); } for(n=0; n< anz_objekte;n++) { fvec[n]->objekt_rand_nach_zeichnen(&dc); } } void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { l_button_down=1; } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { l_button_down=0; CWnd ::OnLButtonUp(nFlags, point); } void CChildView::OnMouseMove(UINT nFlags, CPoint point) { if(l_button_down==1) { for(int n=0; n< anz_objekte;n++) { if(fvec[n]->objekt_test_zeichnen(point)==1) { //Cursor hat innerhalb einer Objektfläche geklickt fvec[n] = new RECHTECK(rechteck_breite[anz_objekte-1], //Fallunterscheidung? rechteck_hoehe[anz_objekte-1] ,0 ,point.x,point.y,obj_fuell_farbe); RedrawWindow(); } } } CWnd ::OnMouseMove(nFlags, point); } void CChildView::OnButtonKreis() { if (objekt_anzahl_test() == 1) { anz_objekte++; fvec[anz_objekte-1] = new KREIS(kreis_rad[anz_objekte-1], kreis_xpos[anz_objekte-1], kreis_ypos[anz_objekte-1],obj_fuell_farbe); RedrawWindow(); } } void CChildView::OnButtonPolygon() { if(objekt_anzahl_test() == 1) { anz_objekte++; vector <koords> polygon(6); polygon[0].x = polygon_xpos[(anz_objekte - 1)*6 ]; polygon[0].y = polygon_ypos[(anz_objekte - 1)*6 ]; polygon[1].x = polygon_xpos[(anz_objekte - 1)*6 + 1]; polygon[1].y = polygon_ypos[(anz_objekte - 1)*6 + 1]; polygon[2].x = polygon_xpos[(anz_objekte - 1)*6 + 2]; polygon[2].y = polygon_ypos[(anz_objekte - 1)*6 + 2]; polygon[3].x = polygon_xpos[(anz_objekte - 1)*6 + 3]; polygon[3].y = polygon_ypos[(anz_objekte - 1)*6 + 3]; polygon[4].x = polygon_xpos[(anz_objekte - 1)*6 + 4]; polygon[4].y = polygon_ypos[(anz_objekte - 1)*6 + 4]; polygon[5].x = polygon_xpos[(anz_objekte - 1)*6 + 5]; polygon[5].y = polygon_ypos[(anz_objekte - 1)*6 + 5]; fvec[anz_objekte-1] = new POLYGON(polygon,obj_fuell_farbe); RedrawWindow(); } } void CChildView::OnButtonRechteck() { if (objekt_anzahl_test() == 1) { anz_objekte++; fvec[anz_objekte-1] = new RECHTECK(rechteck_breite[anz_objekte-1], rechteck_hoehe[anz_objekte-1],0, rechteck_xpos[anz_objekte-1], rechteck_ypos[anz_objekte-1],obj_fuell_farbe); RedrawWindow(); } } void CChildView::OnButtonDreieck() { if (objekt_anzahl_test() == 1) { anz_objekte++; fvec[anz_objekte-1] = new DREIECK(dreieck_h_laenge[anz_objekte-1], dreieck_hoehe[anz_objekte-1], dreieck_fpAbstand[anz_objekte-1], dreieck_xpos[anz_objekte-1], dreieck_ypos[anz_objekte-1],obj_fuell_farbe); RedrawWindow(); } } int CChildView::objekt_anzahl_test() { if(anz_objekte == 10) { MessageBox("Maximale Anzahl an Objekten erreicht! New Button betätigen!","Warnung"); return 0; } else { return 1; } }class CChildView : public CWnd { // Konstruktion public: CChildView(); // Attribute public: // Operationen public: // Überladungen // Vom Klassen-Assistenten erstellte virtuelle Funktionsüberladungen //{{AFX_VIRTUAL(CChildView) protected: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //}}AFX_VIRTUAL // Implementierung public: int objekt_anzahl_test(); virtual ~CChildView(); // Generierte Funktionen für die Nachrichtentabellen protected: //{{AFX_MSG(CChildView) afx_msg void OnPaint(); afx_msg void OnButtonRot(); afx_msg void OnButtonGruen(); afx_msg void OnButtonBlau(); afx_msg void OnButtonSchwarz(); afx_msg void OnButtonWeiss(); afx_msg void OnButtonKreis(); afx_msg void OnButtonPolygon(); afx_msg void OnButtonRechteck(); afx_msg void OnButtonDreieck(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnButtonNeu(); afx_msg void OnButtonGelb(); afx_msg void OnButtonOrange(); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: int obj_fuell_farbe; // 0=Schwarz 1=Rot 2=Blau 3=Grün 4=Weiss int anz_objekte; int l_button_down; int mouse_move; GEOOBJEKTE *fvec[10]; };
-
Es wir nur in OnPaint gezeichnet! Eine Ausnahme kann eine Drag&Drop Operation sein. Hier wird manchmal in WM_MOUSEMOVE gezeichnet.
Wnen eine Methode ein Objekt neu zeichnen will weil sein Status sich geändert hat, dann führt es Invalidate/InvalidateRect aus oder bei Bedarf RedrawWindow.
-
Hallo,
mich würde mal interessieren, wie Michael das mit dem Zeichnen auf einem Hintergrund DC gemeint hat (um Flackern zu verhindern). Ich mache es eigentlich so wie Dragonslayer:void CChildView::OnPaint() { CPaintDC dc(this); zeichneObjekte(&dc); ...Meinst Du damit, man sollte erst auf ein nicht sichtbares Fenster im Hintergrund zeichnen. Wie bekomme ich dann das Ergebnis in mein sichtbares Fenster.
Da ich z.T. sehr viele Objekte zu zeichnen habe, kommt ein Flackern mal vor. Wär schön wenns auch anders geht.Gruss
MiP
-
@MiP: Such mal nach CMemDC! http://www.codeproject.com/gdi/flickerfree.asp
-
hm aber warum kommt es jetzt bei meinem programm zu den oben genannten fehlern?
-
ah ok ich habs jetzt zum laufen bekommen.
Ich hätte da noch ne Frage.
Was ist den der unterschied zwischen
RedrawWindow();
und
Invalidate();
?
-
RedrawWindow löst auch die WM_PAINT Nachricht aus.
Invalidate markiert das Fenster nur als "muss neu gezeichnet werden", es löst WM_PAINT nicht aus. UpdateWindow löst WM_PAINT uas.RredrawWindow ist also (je nach verwendten Flags, siehe Doku), eine Kombination aus InvalidateRect und UpdateWindow.