C# WPF Anwendung um Diagramm erweitern

dapcfreek

Lieutenant
Registriert
Okt. 2008
Beiträge
813
Hallo,

ich habe ein kleines Programm, was mir 5 verschiede Temperaturwerte in Abhängigkeit von der Zeit über eine RS232-Schnittstelle (Usb-RS232 Wandler auf der Platine) von meiner Selbstgebauten Lüftersteuerung übergibt.
Bis jetzt habe ich sie immer über ein ListView anzeigen lassen. Ich würde aber zur besseren Auswertung und vielleicht auch zur Anpassung des Regelkreislaufes die Daten in einem Diagramm darstellen lassen.
Habe bis vor kurzen immer noch mit WinForms gearbeitet, und bin jetzt seit ein paar tagen auf WPF umgestiegen, weshalb ich leider in diesem Bereich noch nicht so fit bin, also bitte nicht verzweifeln, wenn ich es nicht gleich aufs erste mal kapiere^^.
Habe jetzt ein paar Probleme, wo ich mir von euch Hilfe erhoffe:
-Ich habe keine Ahnung, wie ich so ein Diagramm erstellen soll. Gibt es da vielleicht schon was fertiges?
-Habe zum Test mal versucht, die Daten über Excel dazustellen. Da ich aber die Werte nacheinander reinbekomme, ist der Zeitstempel der 5 Sensoren nie gleich, was bedeutet, dass ich die Punkte auf dem Diagramm nie sauber übereinander liegen. Gibt das Probleme bei der Darstellung?

Hoffe, ihr könnt mir helfen und schon mal vielen dank

dapcfreek
 
Hi,

das WPFToolkit (kostenlos) bitet dir das "Charting Toolkit". Dürfte genau für sowas wunderbar passen, habe auch schon damit gearbeitet, in meinem Fall waren Balkendiagramme gewünscht, hat wunderbar einfach funktioniert. Hier mal noch ein kleines Beispiel.

Das Dynamic Data Display könnte auch was für dich sein.

VG,
Mad
 
Super, danke an euch beide. Werde ich Mittag gleich testen, und dann Bericht erstatten :-)
Ergänzung ()

Also,
habe mir jetzt das WPFToolkit ausgesucht. Habe nach dem Beispiel, dass du verlinkt hast das LineChart rausgesucht und habe alles eingefügt.
Funktioniert soweit alles perfekt, auch das Darstellen der Graphen (auch mehrere unterschiedliche). Dafür schon mal einen riesen riesen dank.

Habe jetzt nur noch ein kleines Problem:
Man schreibt ja ((LineSeries)mcChart.Series[1]).ItemsSource =, dann die Werte. Jetzt ist das ja statisch, und ich kann die ja nicht dinamisch hinzufügen. Gibt es da eine Möglichkeit, die Dynamisch hinzuzufügen, z.B. mit .add oder so?

Gruß
dapcfreek
 
Hi,

ich denke, du solltest - wenn du schon auf WPF umsteigst - dich mit den DataBindings vertraut machen. Ohne ein vernünftiges Binding ist WPF ja nicht wirklich sinnvoll. Das ist ein ganz anderer Ansatz als das klassische "WinForm".

Als Tipp: Hier und hier mal lesen.

VG,
Mad
 
Danke nochmal für die Tipps.
Ja, will mich mit dem DataBinding vertraut machen, aber das Programm muss leider die Tage fertig werden, weil es im Neuen PC laufen soll und deswegen muss ich das jetzt irgendwie hinbekommen und dann nach dem Projekt will ich mich mit den ganzen Eigenheiten von WPF richtig auseinander setzen. Dazu gehört natürlich auch DataBinding. Weiß, das ist kein optimaler weg, aber wenn man nicht mehr Zeit hat, muss man halt ein bisschen drixen :-(
 
Hi,

geht jedem mal so ;) Bei den Beispielen von mir sollte eigentlich Code dabei sein den du erstmal so übernehmen kannst. Damit sollte es schonmal gut klappen.

VG,
Mad
 
Also, nochmal vielen dank. Das mit dem Binding ist ja wirklich kein Hexenwerk :D. Kann jetzt alles darstellen, so wie ich es will.
Nur wen ich das ganze jetzt an mein bestehendes Programm einsetze, und ich über die RS232 Schnitstelle Daten empfange und dann verwenden will, bringt er mir folgenden Fehler:
Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet

Die Frage ist jetzt: Läuft das Prog in einem anderen Thread? Bzw. wie kann ich das am einfachsten beheben?
 
Hi,

was greift auf welchen Thread zu? Also was genau machst du?

Stichwort wenn man über Threads hinweg arbeiten will / muss: "invoke".

VG,
Mad
 
Das ist eben das, was ich nicht kapiere. Ich habe eigentlih keine Threads^^. Kann es sein, dass das Programm einen anderen Thread hernimmt?

Habe auch in den using-Direktiven kein System.Threat drin.
 
Zuletzt bearbeitet:
Hi,

was genau hast du denn im Code stehen, kannst du da mal einen Ausschnitt posten? Die GUI z.B. kann durchaus in einem eigenen Thread laufen, ohne dass du das explizit so festlegst.

VG,
Mad
 
Habe hier mal den Code vereinfacht dargestellt. Hoffe, das reicht, und sag genügen aus:
Power ist die Collection, die durch Binding an das Chart gebunden ist.


public partial class MainWindow : Window
{

//Deklaration von Variablen
SerialPort serial = new SerialPort();
ObservableCollection<KeyValuePair<double, double>> Power = new ObservableCollection<KeyValuePair<double, double>>();
public DateTime LogDateTime { get; set; }
#endregion



public MainWindow()
{
InitializeComponent();
showColumnChart();


}


private void btnStart_Click(object sender, RoutedEventArgs e)
{


serial.PortName = "COM5";
serial.BaudRate = 115200;
serial.Parity = Parity.None;
serial.Open();


serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Recieve);

}

private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//Reads every byte in the buffer
while (this.serial.BytesToRead > 0)
{
//Reads the data from the serial port


recieved_data = (byte)serial.ReadByte();

if (ID_bekannt == true)
{
this.LogDateTime = DateTime.Now;
if (ID == "Data1")
{

x++;
Text_Engine1.Write(LogDateTime.ToString("HH:mm:ss"));
Text_Engine1.Write(",");
Text_Engine1.WriteLine(recieved_data);
Power.Add(new KeyValuePair<double, double>(x, Math.Sin(x)));
ID_bekannt = false;
}
//weitere abfragen, was ich weggelassen habe
}
else
{
int deviceType = recieved_data;

switch (deviceType)
{

case 33:
ID = "Data1";
number_of_bytes = 1;
break;


}
ID_bekannt = true;

}
}

}

private void btnClose_Click(object sender, RoutedEventArgs e)
{

App.Current.Shutdown();
}





private void showColumnChart()
{
mcChart.DataContext = Power;
}



private void btnStop_Click(object sender, RoutedEventArgs e)
{
serial.Close();


}


}
}


Danke schon mal
 
Hi,

hast du mal Breakpoint gesetzt und die Variablen ausgeben lassen, bevor der Fehler auftritt?

VG,
Mad
 
Hi,

ja, aber da ist nichts komisch. Ich kann auch feste werte eingeben, und er bringt mir den Fehler. Wenn ich das Power.Add außerhalb dieser Reviece-Methode mache, dann funktioniert das. Das ist eben das, dass ich nicht kapiere. Kann das an der RS232-Schnitstelle liegen? Muss ich die vielleicht in einen anderen Threat auslagern?
 
Hi,

komme gerade ehrlich gesagt nicht so in den Code rein, bin vom Kopf her noch in Arbeit (auch Code) vertieft. Aber du könntest mal folgendes versuchen:

1. Schreibe dir eine public-Methode "addToPower", der du die Werte übergibst und rufe in der Methode das "Power.Add" auf
2. Lies über "invoke" nach und versuche es so

Im Grunde ist es einfach eine Zugriffsrichtlinie, dass ein Thread nicht einfach so etwas in einem anderen Thread anstellen darf. Dafür wäre "invoke" der Schlüssel

VG,
Mad
 
Hi,
also, ich habe mit deiner Hilfe das perfekt hinbekommen. Liegt wohl an dem COM-Port. So wie es ausschaut muss man das mit einem Dispacher machen, wenn man WPF verwenden.
Jetzt habe ich leider noch zwei Probleme. Bin zwar am durchsuchen des gesamten I-nets, aber vielleicht kann mir ja einer einen Tipp geben.
1.ich habe sehr viele Daten (>5000). Dadruch wird das ganze extrem langsam, bzw. es geht irgendwann Garnichts mehr, obwohl die CPU und Ram Auslastung nicht gerade hoch ist. Gibt es da eine Möglichkeit, das abzufangen?
2.Ich habe ja durch Binding das ganze angehängt. Wie kann ich ein zweites Liniendiagramm durch Binding anhängen?
 
Hi,

die Performanceprobleme könnten auf der Tatsache beruhen, dass du bei jeder Änderung gleich das "Add" machst. Baue zuerst das Datenkonstrukt auf und füge dann die kompletten Daten auf einmal hinzu.

Deine zweite Frage verstehe ich nicht ganz - ein zweites anhängen sollte ja genauso gehen wie ein erstes, oder? :) Oder verstehe ich die Frage falsch (wahrscheinlich)? :)

VG,
Mad
 
Hi,

danke für die erste Antwort, ist zwar leider nicht ganz so das, wie ich mir das vorgestellt habe, aber ich werde es mal testen.
Zur zweiten Frage:
Ich habe ein Liniendiagramm, in das kann ich ein zweites einfügen. Wenn ich die Daten jetzt manuell einfüge, so wie in deiner Anleitung oben, dann kann ich einfach mcChart.Series[1] schreiben, und die Daten raufschreiben, also einfach statt null eine 1. Das geht aber bei Data-Binding nicht (zumindest habe ich das noch nicht überrissen :-) )
 
Dann werf ich mal das OpenSource - Framework OxyPlot (gibt auch nen Nuget-Feed) in den Raum. Hier kann man einfach eine Liste im ViewModel halten wo die Funktionswerte gespeichert werden und in der View ist das OxyPlot-Chart-Control auf diese Liste gebunden. Wenn sich die Liste ändert (z.B. Observable List schmeißt Events fürs hinzufügen und löschen von Elementen), ändert sich in der View der Graph...

Somit kannst du verschiedene Arten zur Darstellung deiner Echtzeitwerte umsetzen:
Du füllst die Liste mit einer gewissen Anzahl an Werten. Wenn sie erreicht ist, wird immer der älteste Wert rausgeschmissen und statt dessen an neuerster Stelle der aktuelle eingefügt. Dadurch erhälst du ein wanderndes Fenster auf dem Graphen...
Andere Möglichkeit wäre, die Liste immer wachsen zu lassen, dann werden praktisch immer mehr Werte in den Graphen "gepresst". Dadurch hat man aber alle aufgezeichneten Daten dargestellt...

Viel Spaß!
 
Zurück
Oben