Entwickler-Ecke

Multimedia / Grafik - Linien in Panel zeichnen


c#ler - Fr 20.11.15 11:31
Titel: Linien in Panel zeichnen
Hallo Werte Kollegen,
Ich habe folgendes Problem.
Ich möchte fortlaufend grüne oder rote Linien in ein Panel zeichnen, abhängig davon, ob das Ergebnis einer Sensorprüfung 1 oder 0 ist.
Im Prinzip habe ich das schon erledigt und es funktioniert, jedoch ist die Lösung nicht gerade performant/hübsch.
Ich schreibe mir jedes Ergebnis der Sensormessung in ein Array und rufe bei jedem Schreibvorgang in das Array die Funktion Panel.Invalidate auf, damit das PaintEvent des Panels gefeuert wird.
Das PanelPaintEvent zeichnet dann für jeden Wert im Array eine grüne oder rote Linie in das Panel.
Da die Wertanzahl im Array stetig steigt, füllt sich das Panel allmählich.
Dadurch, dass ich aber für das zeichnen der Linie das Panel immer für ungültig erklären muss, muss ich immer wieder alle Linien aus dem Array zeichnen statt einfach eine hinzuzufügen.

Hat jemand eine Idee, wie ich die lines zeichnen kann ohne jedes Mal das ganze Panel für ungültig zu erklären (Invalidate).
Das sieht in der Praxis nämlich blöd aus, wenn das Panel die ganze Zeit beim schreiben flackert.
Die Anwendung soll eine Livedarstellung der Ergebnisse liefern, daher kann ich leider nicht warten, bis das array eine bestimmte größe hat und dann alles schreiben.
Wäre für Hilfe dazu sehr dankbar :)

Hier nochmal die beiden wichtigsten Methoden / Event

Programmiert wird in c# mit dem VS2010 Prof.
Schreiben in das Array und Anstoss des Zeichnens

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
private void DrawLine()
{
   if (panel.InvokeRequired)
   {
      // Ergebnisse abholen
      received = Client.Receive(ref RemoteIpEndPoint);
      result = Encoding.UTF8.GetString(received);
      results.Add(result);
      bufferPanel1 = results.ToArray();
      panel.Invoke(new Action(() =>
         panel.Invalidate()));
   }
}



PaintEvent des Panels

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
private void panel_Auswertung1_Paint(object sender, PaintEventArgs e)
{
   int i1 = 0;
   if (bufferPanel1 != null)
   {
      foreach (int ie in bufferPanel1)
      {
         i1++;
         if (ie == 1)
         {
            Pen Greenpen = new Pen(Color.Green, 5);
            Graphics g = e.Graphics;
            g.DrawLine(Greenpen, 0 + i1, 490 + i1, 0);
         }
         else
         {
            Pen Greenpen = new Pen(Color.Red, 5);
            Graphics g = e.Graphics;
            g.DrawLine(Greenpen, 0 + i1, 490 + i1, 0);
         }
      }
   }
}


Ralf Jansen - Fr 20.11.15 12:42

Ich würde erstmal DoubleBuffering am Panel auf dem du zeichnest einschalten und schauen ob das schon reicht.

Z.b. ersetzte das Panel durch dieses


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public class DoubleBufferedPanel : Panel
{
    public DoubleBufferedPanel () : base()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
    }
}



DrawLine ist etwas merkwürdig implementiert. Was ist wenn DrawLine nicht aus einem anderen Thread ausgeführt wird? Entweder den if weglassen oder auch den else Zweig implementieren.
Ich bezweifle auch das Invalidate synchronisiert werden muß. Invalidate zeichnet nichts sondern markiert nur zum zeichnen. Zeichnen macht eh der Hauptthread. Gefühlt muß da nicht synchronisiert werden.


c#ler - Fr 20.11.15 13:39

Zitat:
DrawLine ist etwas merkwürdig implementiert. Was ist wenn DrawLine nicht aus einem anderen Thread ausgeführt wird? Entweder den if weglassen oder auch den else Zweig implementieren.
Ich bezweifle auch das Invalidate synchronisiert werden muß. Invalidate zeichnet nichts sondern markiert nur zum zeichnen. Zeichnen macht eh der Hauptthread. Gefühlt muß da nicht synchronisiert werden.


Hallo, Danke das ist dem weiteren Programmablauf geschuldet.
Ich habe für den Post ein paar Sachen, die nicht relevant sind entfernt.
Ich habe 3 panels in 3 Groupboxen.
Ich verschiebe beispielsweise das aktuell beschriebene Panel nach dem vollständigen füllen in eine andere Groupbox.
Dadurch wird immer das oberste der 3 Panels gefüllt und danach 1 nach unten verschoben, bis es wieder oben ankommt.
Wie gesagt, danke für den Kommi.

Habe gerade durch weiteres Probieren und logische Verknüpfungen eine Lösung gefunden:
Ich habe das Zeichnen der Linien aus dem Event gezogen und in meine Thread Hauptmethode eingebunden.
So wird alles nacheinander gezeichnet ohne das Control für ungültig zu erklären.
Dadurch spare ich mir das neuzeichnen aller Lines und füge nur noch die neuen Hinzu bis ich meine Arrayrange erreicht habe.
:)


c#ler - Mo 23.11.15 09:48

Ich habe im direkten Zusammenhang mit dem Thema noch eine Frage:
- Wenn ich drawlines in ein panel zeichne und dann das Parent von dem Panel ändere, werden die Drawlines gelöscht.
Muss ich den letzten Stand der Drawlines nochmal in das Panel nach dem verschieben zeichnen oder gibt es eine Möglichkeit das löschen der Controls im Panel beim 'Umhängen' zu verhindern?


Th69 - Mo 23.11.15 10:25

c#ler hat folgendes geschrieben:
Ich habe das Zeichnen der Linien aus dem Event gezogen und in meine Thread Hauptmethode eingebunden.

Du darfst nur im Paint-Ereignis zeichnen!!!


Ralf Jansen - Mo 23.11.15 10:56

Nebenbei was ist der Sinn hinter dem Panel umhängen?


C# - Mo 23.11.15 14:52

Zitat:

Hat jemand eine Idee, wie ich die lines zeichnen kann ohne jedes Mal das ganze Panel für ungültig zu erklären (Invalidate).

Wenn ich dein Vorhaben richtig deute, dann hast du in deinem Panel eine Art Diagramm und sobald neue Werte zur Datenquelle (dein Array) hinzukommen, willst du dein Diagramm "verlängern". Um nicht alles neu zeichnen zu müssen könntest du eine Überladung von Invalidate nehmen, wie z.B.: Invalidate(Rectangle).
In dem Rectangle gibst du dann den Bereich an, der neu gezeichnet werden soll. Wenn dein Panel also immer in eine Richtung arbeitet (z.B. von links nach rechts), dann merkst du dir nach jedem Update die Position des neuesten Wertes im Panel. Beim nächsten Update zeichnest du dann alles ab diesem Punkt. Natürlich musst du dann auch deine Paint-Methode dahingehend verändern, dass auch immer nur der neueste Wert gezeichnet wird, da die anderen ja immer noch vorhanden sind.

Aber wenn du nur Probleme mit flickering hast dann sollte DoubleBuffer eigentlich ausreichen. Das sollte die Performance nicht sonderlich bremsen, zumal alles über 24Hz Refresh Rate keine qualitativ höherweritge Infos anzeigen kann. Wenn du z.B. alle 10ms einen neuen Wert bekommst, dann bringt es dir nichts alle 10ms (-> 100Hz) Invalidate aufzurufen, das erzeugt nur overheat.