MFC --- Zeichnen mit MoveTo und LineTo?

Hallo,

ich sitze jetzt seit einigen Stunden an einem Problem und mittlerweile ist es halb 3 nachts… O_o
Vielleicht kann mir ja jemand helfen…
Ich habe eine MFC Dialoganwendung erstellt.
In diesem Dialog liegt ein größeres Gruppenfeld (nennen wir es IDC_FELD).
In der Dlg-Klasse habe ich eine Member-Variable m_feld vom Typ CRect.
Über

GetDlgItem(IDC\_FELD)-\>GetWindowRect(&m\_feld);
ScreenToClient(&m\_feld);

(in der Methode OnInitDialog() ^^)

hole ich die Maße vom Gruppenfeld IDC_FELD in die Variable m_feld.
Sobald ich auf den OK-Button drücke, soll in dem Rechteck (Gruppenfeld) ein Raster gezeichnet werden, durchgängig von oben links bis unten rechts, aber nicht im Rest des Dialogs. Die Punkte oben links und unten rechts vom Gruppenfeld kann ich ja nun so bestimmen:

(ab jetzt ist alles in Methode OnOK() …)

int x1 = m\_feld.TopLeft().x;
int y1 = m\_feld.TopLeft().y;
int x2 = m\_feld.BottomRight().x;
int y2 = m\_feld.BottomRight().y;

Jetzt möchte ich mit MoveTo und LineTo halt das Raster in dieses Rechteck zeichnen, aber daran scheitere ich kläglich…
Also:

CPaintDC dc(this);
CPen \*oldpen = NULL;
CPen VioletPen(PS\_SOLID,2,RGB(128,0,128));
oldpen = dc.SelectObject(&VioletPen);
// noch kein Raster zeichnen, nur Linie ausgeben
dc.MoveTo(x1, y1);
dc.LineTo(x2, y2);
dc.SelectObject(oldpen);

Wie ich das sehe, müsste der jetzt ne Linie von oben links schräg nach unten rechts zeichen… Es passiert aber rein gar nichts :frowning:

Ich hab auch noch die Zeilen

InvalidateRect(&m\_feld);
Invalidate();

drunterstehen, aber egal ob ich sie auskommentiere oder stehen lasse, ich sehe keine Linien…
Naja… viel Text für ein vielleicht kleines Problem :smile: (hoffe ich)…
Wär toll wenn da mal jemand drüber nachdenken könnte…

mfG PoiSoN

Hallo,

ich sitze jetzt seit einigen Stunden an einem Problem und
mittlerweile ist es halb 3 nachts… O_o
Vielleicht kann mir ja jemand helfen…

CPaintDC dc(this);
CPen *oldpen = NULL;
CPen VioletPen(PS_SOLID,2,RGB(128,0,128));
oldpen = dc.SelectObject(&VioletPen);
// noch kein Raster zeichnen, nur Linie ausgeben
dc.MoveTo(x1, y1);
dc.LineTo(x2, y2);
dc.SelectObject(oldpen);

Wie ich das sehe, müsste der jetzt ne Linie von oben links
schräg nach unten rechts zeichen… Es passiert aber rein gar
nichts :frowning:

Einen wunderschönen guten Morgen,
Dein Problem ist, dass du einen falschen Device-Context verwendest.
Die KLasse CPaintDC sollte nur für das Zeichnen bei der Behandlung der Message WM_PAINT verwendet werden. Wenn du also beim Klicken auf den Button OK zeichnen willst, solltest du die Klasse CClientDC verwenden.
Also:
CClientDC dc(this);
CPen…
dc.Moveto(x,y);
dc.LineTo (x,y)M

usw.
Dann klappt alles;
Ciao,
Norbert

Einen wunderschönen guten Morgen,
Dein Problem ist, dass du einen falschen Device-Context
verwendest.
Die KLasse CPaintDC sollte nur für das Zeichnen bei der
Behandlung der Message WM_PAINT verwendet werden. Wenn du also
beim Klicken auf den Button OK zeichnen willst, solltest du
die Klasse CClientDC verwenden.
Also:
CClientDC dc(this);
CPen…
dc.Moveto(x,y);
dc.LineTo (x,y)M

usw.
Dann klappt alles;
Ciao,
Norbert

Hallo Norbert,

ich seh Linien, ich bin begeistert !! O_o
Dafür gibts nen Stern, vielen Dank :smile:

Ich dachte eigentlich, dass es egal ist wo ich die Klasse CPaintDC verwende.
Benutzt man sie also nur in den Methoden, die bei Programmstart auf jeden Fall
ausgeführt werden?
Und bei durch den Benutzer ausgelösten Methoden (wie halt ein Bottonklick)
immer CClientDC?
Oder muss ich da auf noch mehr Unterschiede achten?

Cool, jetzt kann ich ja weiter programmieren…
Bis zum nächsten Prob :wink:

cu PoiSoN

Ich dachte eigentlich, dass es egal ist wo ich die Klasse
CPaintDC verwende.
Benutzt man sie also nur in den Methoden, die bei
Programmstart auf jeden Fall
ausgeführt werden?
Und bei durch den Benutzer ausgelösten Methoden (wie halt ein
Bottonklick)
immer CClientDC?
Oder muss ich da auf noch mehr Unterschiede achten?

Cool, jetzt kann ich ja weiter programmieren…
Bis zum nächsten Prob :wink:

cu PoiSoN

Hi, PoiSoN,
Das Problem ist folgendes: OnPaint wird immer aufgerufen, wenn sich Teile des Fensters oder auch das komplette Fenster verändert haben. z.B. eine Messagebox, die Teile verdeckt, die dann danach wieder neu gezeichnet werden müssen. Das erledigt Windows automatisch.
Ein Neuzeichnen erzwingen kannst du mit InvalidateRect oder UpdateWindow().
(Beide senden die Message WM_PAINT an das Dialogfenster)
Wenn du nun selber auf irgend einen Buttonklick zeichnen willst, musst du das also entweder in der Routine OnPaint() machen und die bei Click mittels dieser Funktionen aufrufen, oder eben mittels z.B. CClientDC selbst zeichnen, ohne auf WM_Paint „zu warten“. Dann allerdings bist du auch selbst für ein wieder Neuzeichnen verantwortlich, wenn Teile des Bildschirms zerstört wurden.
Ich hoffe, das macht es ein bisschen klarer.
Ciao,
Norbert

Ein Neuzeichnen erzwingen kannst du mit InvalidateRect
oder UpdateWindow().
(Beide senden die Message WM_PAINT an das Dialogfenster)
Wenn du nun selber auf irgend einen Buttonklick zeichnen
willst, musst du das also entweder in der Routine OnPaint()
machen und die bei Click mittels dieser Funktionen aufrufen,
oder eben mittels z.B. CClientDC selbst zeichnen, ohne auf
WM_Paint „zu warten“. Dann allerdings bist du auch selbst für
ein wieder Neuzeichnen verantwortlich, wenn Teile des
Bildschirms zerstört wurden.

Hallo Norbert,

erstmal danke für die Erklärung.
Ich habe da irgendwie immernoch ein Problem mit meiner Dialoganwendung.
Nach Eintippen von Werten in 3 Eingabefelder und Klick auf den OK-Button werden mittels MoveTo() und LineTo() Linien im Dialog gezeichnet (was ja dank dir auch klappt :smile:…).
Wenn ich die Werte in den Eingabefeldern verändere und wieder auf OK klicke, zeichnet er mir zwar die Linien den neuen Werten entsprechend, löscht aber die alten Linien nicht.
Ich habe schon versucht alles mögliche an den Anfang von OnOK() zu schreiben (Invalidate(), UpdateWindow(), OnPaint(), InvalidateRect(&m_grafik) (

hi PoiSoN,

wie ich schon gesagt habe, wenn du selber zeichnest, dann mußt du auch selber löschen. this->InvalidateRect() erzwingt ein Neuzeichnen des Client Window-Bereichs. D.h. entweder mit den Routinen in OnPaint() oder eben mit dem Default (keine Linien da, also grau und leer -> löschen).
Das sollte eigentlich immer funktionieren, es ei denn dein OnPaint() ist „versaut“ oder du löscht das falsche Fenster. Der this-&gt:stuck_out_tongue_winking_eye:ointer bezieht sich z.B. hier auf die Dialogfeldklasse deines Zeichenfensters.
Die andere Möglichkeit ist, das Ganze wieder mit der Hintergrundfarbe selbst zu überschreiben, was aber nur dann geht, wenn du genau weißt (und dir auch gemerkt hast!), was denn gezeichnet worden ist.

Also z.B:
// zuerst eine Routine, die Linien mit Breite und Farbe zeichnet
// und die vorherige Hintergrundfarbe zurückgibt. (Ist zwar schwachsinnig,
// aber es geht ja ums Prinzip!

int Line (CWnd *wnd,CPoint *P1,CPoint *P2,int color,int width)
{ // Ptr auf Dlg-Klasse, Adr der Points, Breite, Farbe
CClientDC dc(wnd); // DeviceContext
CPen pen(PS_SOLID,width,color),*oldpen; // Pen mit Breite und Farbe
oldpen= dc.SelectObject(&pen); // in den Context wählen
int col= dc.GetPixel (*P1); // col= vorh. (Hintergrund-)Farbe
dc.MoveTo (*P1); // Position anfahren
dc.LineTo (*P2); // Zeichnen
dc.SelectObject (oldpen); // Context restaurieren
return col; // Hintergrundfarbe zurückgeben

CxDialog::open_mouth:nButton1Clicked()
{
int col;
CPoint P1(10,10),P2(100,100); // Start/End-Punkte der Linie
col= Line(this,&amp:stuck_out_tongue_winking_eye:1,&amp:stuck_out_tongue_winking_eye:2,RGB(0xFF,0,0),3); // zeichne Rot, Breite 3 Pixel
// col= die alte Farbe
Sleep (5000); // 5 sec. pennen
MessageBeep(0); // Piep
Line (this,&amp:stuck_out_tongue_winking_eye:1,&amp:stuck_out_tongue_winking_eye:2,col,3); // und schwups - ist sie wieder weg
}

Eine dritte Möglichkeit, ist in eine eigene Bitmap zu zeichnen und diese dann als komplette Zeichnung in einem Rutsch auszugeben. Das erspart dir viel Arbeit!
Ist aber am Anfang auch etwas komplizierter.

wir leiten eine eigene Klasse von CImage ab:

#include // brauchst du für die CImage-Klasse

class CImg : public CImage {// Ableitung von der Classe CImage
public:

void Line (int x1,int y1,int x2,int y2,int col,int w)
{
CDC dc;
dc.SetOutputDC (this->GetDC()); // this ist hier eigentlich unnötig
CPen pen(PS_SOLID,w,col),*oldpen; // es bezieht sich hier auf CImg!!!
oldpen= dc.SelectObject(&pen);
dc.MoveTo (x1,y1);
dc.LineTo (x2,y2);
dc.SelectObject(oldpen);
dc.ReleaseOutputDC(); // wieder aufräumen
this->ReleaseDC();
}

void Out (CWnd* wnd,CRect& rcdst) // Ausgabe der Bitmap
{ CClientDC dc(wnd);
HDC hdc= dc.m_hDC;
this->Draw(hdc,rcdst); // wieder this -> CImg
}
};

In deiner Routine also dann:

// Erzeuge Bitmap mit 500x500 Pixeln und 32 Bit Farbtiefe
// Zeichne Dreieck in 3 Farben auf schwarzem Hintergrund

CxDialog::open_mouth:nButton1Clicked()
{
CRect rc(0,0,500,500);
CImg I; // deklarieren
I.Create (500,500,32,0); // und initialisieren
I.Line (10,10,100,100,0x0000FF,3); // Dreieck zeichnen rot
I.Line (100,100,50,50,0x00FF00,3); // grün
I.Line (50,50,10,10,0xFF0000,3); // blau
I.Draw (this,rc); // ausgeben (this= hier CxDialog)
Sleep (5000);
this->InvalidateRect (rc); // und futsch
}

für genauere Informationen lies die einfach mal die Hilfe zu CImage durch. Da kann man ziemlich viel damit machen.

Viel Spasse,

Norbert