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.
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.
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.
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.
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
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 :-)
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.
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.
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?
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.
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?
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 ;)