Forum: PC-Programmierung Visual Studio C++ MFC meine eingene drawing class


von David (Gast)


Lesenswert?

Hallo Leute


Ich habe eine Frage zum Konzept, Visual Studio MFC betreffend. Bisher 
habe ich ein MFC SDI C++ Projekt erstellt nun möchte ich in die Client 
Area etwas zeichnen. Genauer gesagt habe ich in der Client Area 
verschiede Elemente eines davon ist ein Singnal Plot über die Zeit, 
ähnlich einem Oszilloskop. Nun möchte ich für jeden Bereich des Client 
Areas eine eigene Klasse erstellen.
Beispielsweise eine Klasse CSignalPlot, in dieser Klasse werden alle 
Berechnungen und Datentransfers, die das Signal betreffen durchgeführt. 
Nun möchte ich von dieser Klasse aus das Signal auf dem Bilschirm 
ausgeben. Nun weiss ich nicht wie dies funktioniert, irgendwie brauche 
ich eine OnDraw() Methode...

Ich wäre froh um einige Inputs; ist mein Ansatz korrekt? Kann ich für 
jeden Bereich in meinem Client Fenster eine Klassse erstellen und so 
meinen Code klar strukturieren? Oder wie entspricht dies nicht dem MFC 
Konzept?

Anmerkung: Ich möchte vermeiden dass ich den ganzen Code zum Zeichnen in 
der CmyprojectView Klasse einfügen muss, das erscheint mir sehr 
unübersichtlich und unstrukturiert.

von Karl H. (kbuchegg)


Lesenswert?

David schrieb:

> Ich wäre froh um einige Inputs; ist mein Ansatz korrekt? Kann ich für
> jeden Bereich in meinem Client Fenster eine Klassse erstellen und so
> meinen Code klar strukturieren? Oder wie entspricht dies nicht dem MFC
> Konzept?

Zunächst mal ist einer der wichtigsten Punkte in der Document-View 
Architektur, die klare uns konsequente Trennung zwischen Document und 
View. Ein Document hält die Daten. Punkt. D.h. die Messwerte deines 
Signals sind im Document beheimantet (und da mglw. wieder in einer 
Klasse, von der du ein Objekt als Member im Document hältst).
Um die Daten anzuzeigen, gibt es eine (oder mehrere) View Klassen.

Das Document hält die Daten, die View entscheidet, wie sie anzuzeigen 
sind.

> Anmerkung: Ich möchte vermeiden dass ich den ganzen Code zum Zeichnen in
> der CmyprojectView Klasse einfügen muss, das erscheint mir sehr
> unübersichtlich und unstrukturiert.

Niemand hindert dich, für einen bestimmten Anzeigetyp eine 
Diagrammklasse zu machen, die weiß, wie man ein Diagramm anzeigt 
(inklusive Achsen und Beschriftungen). Dieses Diagramm kriegt von der 
View den Datencontainer mitgegeben, den es darstellen soll und das 
Diagramm weiß auch, wo auf dem HDC es sich malen soll, wenn seine OnDraw 
Methode von der OnDraw Methode des View aufgerufen wird.

Und auch ja. Im Prinzip hindert dich auch niemand, auf einem View 
mehrere Diagramme gleichzeitig an unterschiedlichen Stellen anzuzeigen. 
Obwohl es meist vernünftiger ist, für jedes Diagramm ein eigenes Fenster 
aufzumachen.


> irgendwie brauche ich eine OnDraw() Methode...

Die View hat eine. Die wird auch aufgerufen. Die VIew kann jetzt selber 
was zeichnen, oder sie kann natürlich auch auf anderer Klasse 
weitergeben, denen den CDC zur Verfügung stellen und die malen dann da 
drinn rum. Zum Beispiel die Achsen eines Diagramms. Oder die Daten des 
Datencontainers, die sich der View vom Document holt. Wobei dich wieder 
nichts und niemand daran hindert, für bestimmte Teilaspekte eine eigene 
KLasse zu kreieren. Es spricht nichts dagegen, zb für ein Raster eine 
Klasse einzusetzen, die dann eben in den Draw-Prozess eingebunden wird, 
indem die von der Klasse zur Verfügung gestellte OnDraw Methode von 
'höherer Instanz aus' aufgerufen wird.

Aber überleg dir gut, welche Daten
* Dokument-spezifisch
* View-spezifisch
sind.
Diese Trennung ist das um und auf in der Document-View Architektur.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

David schrieb:
> Ich möchte vermeiden dass ich den ganzen Code zum Zeichnen in
> der CmyprojectView Klasse einfügen muss, das erscheint mir sehr
> unübersichtlich und unstrukturiert.

So ist es aber vorgesehen.

Das macht nix, der OnDraw-Handler des Views kann ja die Draw-Funktionen 
Deiner zum Dokument gehörenden SignalPlot- und weiß-nicht-was-Objekte 
aufrufen.

Damit hast Du Deinen zum SignalPlot-Objekt gehörenden Draw-Code dort, wo 
Du ihn haben willst, nur daß das kein OnDraw-Handler mehr ist, sondern 
eine von Dir vorgegebene Draw-Funktion, der natürlich vom View der 
gewünschte Devicekontext zu übergeben ist.


Bist Du Dir sicher, daß Du Dich jetzt noch in die MFC einarbeiten 
willst? Wenn Du als Anfänger mit einer GUI-Klassenbibliothek arbeiten 
möchtest, würde ich Dir raten, um die MFC einen Bogen zu machen, weil 
sie a) ziemlich schlecht dokumentiert, b) ziemlich unübersichtlich und 
c) nur auf genau eine Plattform beschränkt ist.
Von der Denkweise her recht nah verwandt ist wxWidgets, das den Vorteil 
hat, auch unter Linux und OS X verwendet werden zu können.

von Karl H. (kbuchegg)


Lesenswert?

Rufus Τ. Firefly schrieb:

> Bist Du Dir sicher, daß Du Dich jetzt noch in die MFC einarbeiten
> willst? Wenn Du als Anfänger mit einer GUI-Klassenbibliothek arbeiten
> möchtest, würde ich Dir raten, um die MFC einen Bogen zu machen, weil
> sie a) ziemlich schlecht dokumentiert, b) ziemlich unübersichtlich und
> c) nur auf genau eine Plattform beschränkt ist.

d) in die Jahre gekommen. Böse Zungen würden auch Auslaufmodell sagen
e) eigentlich nicht besonders gut aufgebaut, was zum Teil den 
beschränkten Fähigkeiten des C++ Compilers aus dem Jahre 1992 (bzw. 
früher) geschuldet ist.

Persönlich verwende ich die MFC immer noch gerne. Nach all den Jahren 
ist es einfach ein Framework, dass ich kennen gelernt habe und mit dem 
ich gut umgehen kann. Empfehlen würde ich sie trotzdem niemand mehr, 
ausser vielleicht als Framework um sich in die Document-View Architektur 
einzuarbeiten. Aber da gibt es auch andere (bessere) Möglichkeiten.

von David (Gast)


Lesenswert?

Danke für die rasche Anwort, dies ist sehr hilfreich.

Die genannten Nachteile von MFC habe ich am Rande bereits etwas 
mitgekriegt, habe sie jedoch als Gerücht abgetan :/...mein Fehler.
Ich habe MFC gewählt, da mein Vorgänger damit gearbeitet hat.

Womöglich könnt Ihr mir helfen eine passendere Grundlage für mein 
Programm zu finden. (Anstelle von MFC)
Vorgaben:
a) Das Programm muss in Visual Studio in C++ geschrieben werden.
b) Die Datenverarbeitung muss so schnell wie möglich sein, es werden 
"grössere" Mengen an Daten analyziert und geplotet.
c) Es dürfen keine merklichen Verzögerungen zwischen Userinteraktionen 
und Reaktion vom Program bestehen (ist vermtlich eine Frage von meiner 
Implementierung).
c) Das Programm muss nicht Platformunabhängig sein. (falls dies 
überhaupt möglich ist mit Visual C++)

Noch einige Worte zum Programm:
Das Programm ist eine Usersoftware zur Anylsierung von EKG Signalen, es 
werden EKG Signale von langer Dauer mithilfe verschiedener Algorithmen 
analysiert und dargestellt. Der User kann mithilfe der Analyseresultaten 
einen Report erstellen und diesen speichern/drucken. Verschieden Files 
müssen geladen und gepseichert werden können.

Für weitere Hilfestellungen wäre ich äusserst Dankbar :)
Gruss David

von Karl H. (kbuchegg)


Lesenswert?

David schrieb:

> Ich habe MFC gewählt, da mein Vorgänger damit gearbeitet hat.

Heißt das, das Pgm existiert schon und du baust es weiter aus?
Denn in dem Fall tut sich der Fragenkomplex auf: Verwirft man ein 
bestehendes Programm bzw. schreibt es neu? Mit neuen Programm-Fehlern.
Und das ist nicht gut.
Ganz abgesehen davon, dass man eine derartige Aktion bei den 
Projektverantwortlichen bzw. Firmenleitung nur schwer durchdrücken kann, 
sofern es nicht massive Vorteile verspricht.
Denn ein 'Neu-schreiben' kostet erst mal Geld, ohne dass man etwas von 
aussen ersichtliches dafür bekommt. Das ist nichts, was die 
Verkäufertruppe draussen verkaufen kann.

> a) Das Programm muss in Visual Studio in C++ geschrieben werden.
> b) Die Datenverarbeitung muss so schnell wie möglich sein, es werden
> "grössere" Mengen an Daten analyziert und geplotet.
> c) Es dürfen keine merklichen Verzögerungen zwischen Userinteraktionen
> und Reaktion vom Program bestehen (ist vermtlich eine Frage von meiner
> Implementierung).
> c) Das Programm muss nicht Platformunabhängig sein. (falls dies
> überhaupt möglich ist mit Visual C++)

Deine 'Anforderungspunkte' sind nicht wirklich die ausschlaggebenden 
Kriterien. Denn die werden auf heutigen PCs mit Leichtigkeit erfüllt.
Das Problem mit der MFC ist ganz einfach eine Frage der 
Zukunftstauglichkeit. Deshalb sind Neuentwicklungen mit der MFC nicht 
mehr zielführend.

> Das Programm ist eine Usersoftware zur Anylsierung von EKG Signalen, es
> werden EKG Signale von langer Dauer mithilfe verschiedener Algorithmen
> analysiert und dargestellt. Der User kann mithilfe der Analyseresultaten
> einen Report erstellen und diesen speichern/drucken. Verschieden Files
> müssen geladen und gepseichert werden können.

Das ist alles recht Standard und nichts aussergewöhnliches. Ist mit 
jedem Framework machbar.
Persönlich würde ich wahrscheinlich zu Qt greifen. Aber auch nur 
deswegen, weil ich das Framework schon kenne. Abgesehen davon hab ich da 
keine Präferenzen in diese Richtung.
'Das Beste' ist immer das, womit man umgehen kann :-)

: Bearbeitet durch User
von David (Gast)


Lesenswert?

Karl Heinz schrieb:
> Heißt das, das Pgm existiert schon und du baust es weiter aus?
> Denn in dem Fall tut sich der Fragenkomplex auf: Verwirft man ein
> bestehendes Programm bzw. schreibt es neu?

Das existierende Programm ist nur ein Entwurf, es ist sehr schlecht 
strukturiert und meine Aufgabe ist es, das Programm neu zu schreiben und 
zu erweitern, in einer klaren Struktur (Klassen und so).
Dennoch werde ich versuchen möglichst viele Implementierungen aus dem 
existierenden Programm zu übernehmen.

von Hans-Georg L. (h-g-l)


Lesenswert?

Ich habe kürzlich für mich etwas ähnliches programmiert aber ich arbeite 
privat lieber mit der WTL und GDI+ geht aber auch mit der MFC und GDI+.
Da habe ich auch verschiedene Bereiche in meinem View, die sich auch 
dynamisch ändern können. Du musst nur beim Programmstart die GDI+ 
initialisieren und am Programmende wieder beenden, das ist alles. Und 
natürlich die Header einbinden ;)

Also habe ich eine virtuelle Oberklasse, für meine Zeichenobjekte, mit 
der Methode GetPath() geschrieben, die einen GrapicsPath zurückliefert. 
Das hat den Vorteil, das deine abgeleiteten Klassen nichts selbst 
zeichnen sondern nur grafische Objekte (Linien, Kurven, Texte usw.) zu 
dem Graph hinzufügen. Der View klappert die dynamische Liste mit den zu 
zeichnenden Objekten ab, holt sich den Path von jedem Objekt, skaliert 
und verschiebt ihn mit Hilfe einer Transformationsmatrix  an den 
richtigen Platz und zeichnet ihn dort. Zusätzlich noch double buffering 
und fertig. Das ist mit der GDI+ wirklich total easy. Für Scrolling und 
Zooming brauchst du nur die Transformationmatrix ändern, und nichts bei 
den Objekten neu berechnen.

von David (Gast)


Lesenswert?

An: Hans-Georg Lehnard

Ich denke das mit dem GDI+ kriege ich hin.

Doch was Sie noch beschrieben haben mit der virtuellen Oberklasse klingt 
für mich sehr interessant, doch es scheint etwas mein Programierwissen 
zu überschreiten.
Könnten Sie mir womöglch noch etwas auf die Sprünge helfen?

Wie ich es verstanden habe, kann ich für jedes Element in meinem Client 
Bereich (also Beispielsweise für den Signalplot) eine  Klasse von dieser 
virtuellen Oberklasse ableiten (Bsp.: class CsignalPlot : public 
DievirtuelleOberklasse).
In dieser virtuellen Oberklasse gibt es die Methode GetPath() (die ist 
selbstgeschrieben nehme ich mal an?). Diese GetPath() Methode rufe ich 
in der OnDraw() Methode auf, um den Graphicspath, der den Signalplot 
enthält, zu erhalten.
Ist dies soweit korrekt?
Wo implementiere ich nun das Doublebuffering?

von Hans-Georg L. (h-g-l)


Lesenswert?

Das mit der Ableitung ist so richtig. Der Sinn dahinter ist, das der 
View nur die virtuelle Oberklasse kennen muss mit der Methode Getpath() 
und deshalb nicht angepasst werden muss, wenn mal eine neue abgeleitete 
Klasse dazu kommt. Der GraphicPath braucht auch keinen Graphic Context 
und muss deshalb nicht innerhalb einer Draw Methode erstellt/geändert 
werden.

Für das double Buffering erzeugst du in deinem View einen (kompatiblen) 
Memory Context zeichnest in diesen und kopierst dann den Memory Context 
auf deinen Bildschirm. Damit verhinderst du flackern beim Zeichnen.
Dafür gibts genügend Beispiele im Netz.

Das einzige Problem mit der GDI+ ist, das alle Bücher und der Großteil 
der Onlinedokumentationen für .NET geschrieben sind. Aber wenn du mal 
begriffen hast, wie die C++ Syntax sich von der .Net Syntax 
unterscheidet ist das auch kein Problem mehr.

: Bearbeitet durch User
von David (Gast)


Lesenswert?

Danke für die Hilfe, ich werde dies so umsetzen.
Dieses Forum ist für mich bisher unübertroffen :)


Schönen Tag David

von David (Gast)


Lesenswert?

So, ich habe mich die letzen Stunden mit diesem Problem beschäftigt, nun 
sind noch einige Fragen aufgetaucht.

Ich erstelle einen Graphicspath in einer Zeichnen-Klasse ( welche von 
meiner virtuellen Oberklasse abgeleitet ist) und füge diesem Path meine 
gewünschten Figuren zu.
Anschliessend rufe ich in meiner CmyprojectView::OnDraw() Methode meine 
GetPaht() Mehtode auf und bekomme den GraphicsPath zurück.
Diesen GraphicsPath gebe ich nun auf den Bildschirm aus
1
graphics.DrawPath(myPen,myGraphicsPath)

Das Problem ist, ich möchte ein ganzes Bild in meiner Zeichnen-Klasse 
erstellen mit Hintergund, Achsen, Signallinie und das ganze in 
verschiednen Farben und nicht nur Shapes. Anschliessend möchte ich 
dieses ganze Bild an die CmyprojectView::OnDraw() Methode übergeben und 
dort ausgeben.
Wie kann ich das machen?

von Hans-Georg L. (h-g-l)


Lesenswert?

Du wolltest mehrere (unabhängige) Bereiche oder ?

Aber in Wirklichkeit willst du nur die Zeichenbefehle, die normalerweise 
im View stehen in eine eigene Klasse packen. Das macht meines Erachtens 
nicht viel Sinn, denn du gewinnst dadurch überhaupt nichts und hast nur 
eine zusätzliche Klasse und einen zusätzlichen Methodenaufruf.

Vergiss alles was ich geschrieben habe und schreib dir dann eine Klasse 
mit einer Draw Methode welche alles malt was du willst. Du musst nur 
dieser Draw Methode die Client Grösse deines Views und den Grafic 
Context übergeben.

Diese Klasse solltest du dann aber "IchMaleAllesWasDerViewSonstMalt" 
taufen ;)

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.