Autor Beitrag
steg14
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 03.04.10 00:42 
Hallo zusammen,

ich bin neu hier und auch ein neuer C#-Programmierer.
Ich habe mir #D runtergeladen - gefällt mir gut.
Das Forum hier macht einen guten Eindruck - Glückwunsch.

Nun zu meinem Problem:
Ich will eine Simulation erstellen.
Dazu sollen verschiedene Maschinenteile in jeweils einer PictureBox im selben Formular bewegt werden.
Für die Bewegung jedes Teiles ist jeweils ein Thread zuständig.
Die Bewegungen müssen nicht sychronisiert werden.
Die Threads sollen von Komponenten des Formulars (Timer oder Buttons) gesteuert werden.

Nun habe ich schon ein paar Stunden gegoogelt und dabei festgestellt, dass es wohl verschiedene Möglichkeiten
(wie Invoke/Delegate, Backgoundworker oder Dispatcher) gibt.

Ein funktionierendes Beispiel habe ich noch nicht gefunden - meistens nur Konsolenanwendungen.

Könnt ihr mir bitte einen Tipp geben wie ich prinzipiell vorgehen soll.

Gruß
Stefan
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 03.04.10 01:05 
Ich bin in C# auch noch nicht so sicher, dass ich jetzt ein schnelles Beispiel aus dem Hut ziehen könnte, aber grundsätzlich erst einmal folgendes:
Du kannst aus einem Thread nicht direkt auf Elemente des Fensters zugreifen. Jegliche Zugriffe auf andere Teile des Programms aus dem Thread müssen synchronisiert werden, das heißt im Kontext des "Hauptthreads" ablaufen.

Die Frage ist daher wofür du die Threads nutzt. Für die Darstellung von bewegten Bildern als Animation? Dann eignet sich da ein Timer besser, da der direkt zugreifen kann und die Threads nichts wirklich im eigenen Thread machen können. Und ein Thread bringt nichts, wenn er jede Aktion mit dem Hauptthread synchronisiert durchführen muss.
steg14 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 03.04.10 10:13 
Ja die Threads sollen im Kontext des Hauptthreads ablaufen aber asynchron.

Die Bilder sollen sich als eigenständige Objekte eben selbsständig und unabhängig voneinander im Formular bewegen können. Dabei müssen sie aber auf Ereignisse im Formular reagieren und Daten im Formular ändern.

Die Sache mit dem Timer verstehe ich nicht
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 03.04.10 11:06 
user profile iconsteg14 hat folgendes geschrieben Zum zitierten Posting springen:
Die Sache mit dem Timer verstehe ich nicht
Nun, du kannst die Bewegung auch realisieren, indem du per Timer alle z.B. 25 Millisekunden die Position der Objekte usw. aktualisierst. Und der Code in den Timer Events wird ohnehin im Hauptthread ausgeführt. so dass es erst gar keine Probleme gibt.
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Sa 03.04.10 11:10 
Ich bin nicht ganz so neu in C# und so ganz verstehe ich nicht, warum du alles in unterschiedlichen Objekten handhaben möchtest? Warum nicht alles in einer PictureBox animieren?

Mal davon abgesehen kannst du wahrscheinlich auf das ganze Threading verzichten, wenn du mit den Threads nur ein paar Pictureboxen bewegst. Du schreibst ja, das die Bewegung von Buttons oder Timern gesteuert wird. Solche Aktionen laufen i.d.R. schneller ab, als die Prozessorzeitplannung für einen solchen Thread vorsieht, womit der Thread seinen Sinn verliert. Threads sind nur sinnvoll für stark rechenaufwendige Prozesse oder Prozesse, die in einer Schleife laufen. In diesem Fall überläßt man es Windows, den Thread ab und an zu stoppen, um anderen Threads (wie z.B. die Verarbeitung der MessageLoop des Programms) die Möglichkeit zu geben, ihre Aufgaben zu erledigen.

Du kannst ja erstmal erklären, warum du alles in Threads machen willst und was du genau in den Threads machen möchtest, dann können wir dir auch sagen, was wohl die beste Methode wäre oder ob es überhaupt sinnvoll ist, alles in Threads zu erledigen.
steg14 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 03.04.10 11:16 
Dann müsste ich Timergesteuert aber komplett alle Objekte praktisch in einer Schleife überwachen.
Ich müsste überlegen welche Ereignisse welche Auswirkungen auf ein Objekt haben und ensprechen die neue Position nach 25ms bestimmen.
Ginge sicher, aber diese Vorgehensweise entspricht nicht meinen Vorstellungen von OOP ist eher eine getarnte prozeduale Programmierung.
Ausserdem will ich ja gerade lernen mit Threads umzugehen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 03.04.10 11:22 
user profile iconsteg14 hat folgendes geschrieben Zum zitierten Posting springen:
Dann müsste ich Timergesteuert aber komplett alle Objekte praktisch in einer Schleife überwachen.
Nein, musst du nicht. Du gehst einfach im Timer die Objekte durch und sagst denen, dass sie sich aktualisieren sollen. Was die dann draus machen, steht dann in dem Objekt selbst. ;-)

Und wenn du Threads benutzen möchtest, dann ist wirklich die Frage was du dir davon versprichst. Denn einfach nur zum Üben die schlechtere und unpassende Variante nehmen kannst du natürlich, aber dann gewöhnst du dir das später vielleicht an.
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Sa 03.04.10 11:49 
Threads wo sie sinnvoll sind. Deshalb habe ich geschrieben, du sollst mal erklräen, was so ein Thread genau macht...

Naja, Threads haben erstmal nicht soviel mit OOP zu tun. Mit OOP ist die Stucktur der Programmierung gemeint. Stellen wir uns mal vor, du programmierst ein Auto. Dafür erstellst du eine Klasse die Auto heißt. Die hat dann verschiedene Funktionen, soetwas wie "Fahren", "Bremsen", "Starten" etc. Das Auto hat dann wiederum weitere Komponenten wie z.B. "Motor", "Bremsen", "Türen", "Sekundärelektrik" etc. Das wären wieder eigene Klassen, die Teil des Autos sind. Der Motor besteht wieder aus Funktionen und Unterklassen ("Anlasser", "Motorblock"). Es gibt Funktionen und Eigenschaften, die alle Komponenten haben (z.b. "Gewicht", "Fallgeschwindigkeit"), die praktisch geerbt werden und für jede Komponente gibt es spezifische Eigenschaften. Das kann man runterbrechen bis auf jede einzelne Schraube. Also, man Konstruiert sein Auto Programmtechnisch, wie man es Konstruktiv auch aus Objekten zusammensetzt. Nur das man in der Programmierung noch mit Funktionen versehen kann. Das Auto kann man dann wieder in ein übergeordnetes Objekt, z.B. eine Simulationsumgebung einsetzen. Das ist mal so grob OOP. Das hat mit Threads erstmal nicht zu tun.

Thread dienen "lediglich" dazu, ansynchrone Vorgänge auszukoppeln. Das macht man zum Beispiel, damit die grafische Oberfläche bedienbar bleibt, während einer Berechnung. Es gibt noch weitere Gründe, Threads zu verwenden, wie z.B. die Verarbeitung auf mehreren Prozessoren etc. Aber prinzipiell kann man sagen, das Prozesse, die in weniger als der Prozessorzeit für einen Thread ablaufen, nicht in Threads ausgelagert werden brauchen. Damit verlangsamst du dein Programm nur sinnlos.

Zitat:
Dann müsste ich Timergesteuert aber komplett alle Objekte praktisch in einer Schleife überwachen.


Wenn du für jedes Teil ein eigenes Objekt hast, hat dieses Eigenschaften, die du frei programmieren kannst. Eine Abfrage des Zusatndes musst du sowieso früher oder später machen... im Prinizip sollte der Code in deinem Timer kein anderer sein, als in dem Thread. Lediglich der Aufruf verändert sich.

Zitat:
Ich müsste überlegen welche Ereignisse welche Auswirkungen auf ein Objekt haben und ensprechen die neue Position nach 25ms bestimmen.


Machst du das in deinen Threads nicht? Wie bestimmst du die Position in den Threads?
steg14 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 03.04.10 12:54 
hmmmm --- ihr habt mich fast überzeugt.

trotzdem - hier ein simples Beispiel von dem was ich im Prinzip will (Bild):

Zwei Objekte (rot und blau) sollen sich je nach Vorgaben in den Textfeldern bewegen.

Ich dachte mir also jedes Objekt ist ja ein wirkliches Objekt im Sinn der OOP. Da ich jedoch keine Klasse "roteKugel" instanzieren kann weil ja alles in einer Form laufen soll. Wollte ich dennoch jede Kugel unabhängig voneinander in einem Thread laufen lassen. Die Methode die dieser Thread dann ausführt, soll in der Textbox nachsehen, ob sich was geändert hat.

Ihr meint also ich solle ins Formular eine Timer setzen der alle 25ms nachsieht welchen Status die Textfelder haben und dann die neue Position der Kugeln nach 25ms berechnet und aktualisiert? Ist das so korrekt?
Dann würde also die Steuerung von später mal 100 Bällen komplett in der Timermethode erfolgen?

Wie geht das mit mehreren Kugeln in einer Picturebox?
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 03.04.10 13:43 
user profile iconsteg14 hat folgendes geschrieben Zum zitierten Posting springen:
Ihr meint also ich solle ins Formular eine Timer setzen der alle 25ms nachsieht welchen Status die Textfelder haben
Nein, wenn sich da etwas ändert, dann bekommst du das ja über das entsprechende Ereignis mit, das ist Blödsinn da ständig nachzuschauen. Es reicht bei Änderungen dort das entsprechende Objekt darüber zu informieren.
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Sa 03.04.10 18:32 
Zitat:
Da ich jedoch keine Klasse "roteKugel" instanzieren kann weil ja alles in einer Form laufen soll


Wieso kannst du keine Klasse rote Kugel Instanzieren? Hä? Du brauchst wohl erst einmal einen kleinen Grundkurs darüber, was OOP überhaupt ist, und wie man so etwas programmiert.

Also, so erstellt man Klassen

Das wäre ein Beispiel für eine Basisklasse, das stellt einfach mal nur ein physikalisches Object dar. Jedes physikalische Object, ob Kugel oder Würfel oder ein Auto hat ein paar grundlegende Eigenschaften. Wir beschränken uns hier mal auf die Dichte (rho) und wegen mir die Position. Es gibt natürlich viel mehr oder ganz andere Eigenschaften, hengt von der Problemstellung ab.

ausblenden 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:
24:
25:
public class PhysicalObject

    private double rho;
    private Point location;

    public double Rho
    {
        get { return rho;}
        set { weigth = value; }
    }
    
    public Point Location
    {
        get { return location;}
        set { location = value; }
    }

    public PhysicalObject(double rho)
    {

        this.rho = rho;
        this.locatation = new Point();
    }
    
}


Eine Kugel ist ein physikalisches Objekt. Es erbt also die Eigenschaften die ein physikalisches Objekt hat. Das wird mit dem Doppelpunkt gemacht. Unsere Kugel hat jetzt alle Eigenschaften, die wir oben schon verliehen haben, ohne das wir sie nochmal Programmieren müssen. Jetzt müssen wir nur noch das Implementieren, was die Kugel ausmacht.

ausblenden volle Höhe 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:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
public class Kugel : PhysicalObject
{
   private double radius;
   private Bitmap shape;

   public double Radius
   {
      get{ return radius;}
      set{ 
           radius = value;
           shape = new Bitmap(2*radius, 2*radius);
           using(Graphics g = Graphics.FromImage(shape))
           { 
       gDrawEllipse(new Pen(Color.Blue),radius, radius, radius, radius);
           }
  }
   }

   public double Volumen
   {
      get{ return 4d/3d * radius * Math.Pi;}
   }

   public double Masse
   {
      get{ return Volumen * Rho;}
   }

   public Bitmap Shape
   {
      get{ return shape;}
   }

   public Kugel(double radius, double rho)
     : base(rho)
   {
      this.Radius = radius;
   }
}


Eine Kugel wird allein durch ihren Radius bestimmt. Sie hat natürlich noch mehr Eigenschaften, wie Volumen, Masse etc. Aber die hengen allein vom Radius ab. Also haben wir jetzt ein Object Kugel. Mit der Eigenschaft Shape haben wir ihr noch ein Aussehen verliehen. Man kann hier natürlich auch irgendein Bild oder so zuweisen. Der einfachheit halber nehmen wir jetzt erstmal als Längeneinheit Pixel an. Die Dichte ist dann entsprechend in kg/Pixel^3.

So, kommen wir nun mal zu unserer Form. Hier haben wir wieder so ein typisches Beispiel für Vererbung. Deine Klasse ("MyForm") erbt nämlich die Eigenschaften von Form. Und du kannst hier jetzt diesem neuen Objekt, dessen Erschaffer du bist, noch Eigenschaft, Steuerelemente etc. hinzufügen, ohne dich um alles kümmern zu müssen. Wäre es nicht so, müsstest du dich zum Beispiel selber darum kümmern, das Fenster auf dem Bildschirm zu zeichnen,einen Schließen-Button hinzuzufügen usw.

ausblenden volle Höhe 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:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
public class MyForm : Form
{

   private Kugel[] meineKugeln;

   public MyForm()
   {
       //Neues Array von Kugeln
       InitializeComponents();
       meineKugeln = new Kugel[3];

       meineKugeln[0] = new Kugel(1000.1);
       meineKugeln[1] = new Kugel(1200.2); 
       meineKugeln[2] = new Kugel(1500.1); 
   }

   public DrawKugel(int ID, int X, int Y)
   {
       using(Graphics g = this.CreateGraphics())
       {
          g.DrawImage(meinKugeln[ID].Shape, X, Y);
       }
   }

   private button1_Click(object sender, EventArgs e)
   {
      DrawKugel(10,0);
   }

   private button2_Click(object sender, EventArgs e)
   {
      DrawKugel(2150,150);
   }

   private button3_Click(object sender, EventArgs e)
   {
      DrawKugel(30,300);
   }
}


In unserer Form habe wir drei Buttons. Wenn du im Designer darauf einen Doppelclick machst, dann kommst du in das Button-Click Event, mit dem wir unsere Kugel (eigentlich ja ein Kreis) malen. Genauso könnte man das TextChanged-Event einer Textbox nutzen, die Location parsen und den Kreis malen... Da braucht man echt keine Threads für.


PS. Der Code ist nicht getestet, weil ich hier kein VS auf dem Rechner habe, also kein Gewähr für Fehlerfreiheit. Aber das Prinzip sollte klar sein...
steg14 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 03.04.10 21:04 
erst mal vielen Dank Norman, du hast dir viel Arbeit gemacht. :beer:

Das Prinzip der Vererbung ist mir schon klar. In der Prxis gibt es aber viele Probleme:
(Inzwischen bin ich auf Visual C# Express umgestiegen)
z.B. soll die Entwicklingsumgebung eine neue Klasse erstellen oder schreibe ich den Code einfach in "Form1.cs"?
solche Dinge beschäftigen mich.
Oder wenn ich Beispiele im Netz testen möchte, erhalte ich viele Fehlermeldungen.
Wie z.B. auch bei deinem Code - da steht dann
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
   public DrawKugel(int ID, int X, int Y)
   {
       using(Graphics g = this.CreateGraphics())
       {
          g.DrawImage(meinKugeln[ID].Shape, X, Y);
       }
   }
{

Fehler:"erwarte Rückgabewert" keine Ahnung wieso!

Inwischen habe ich aber die Sache mit den Threads zum Laufen gebracht. Ist aber wie ihr schon geschrieben habt sehr langsam.
Ich verwende:
ausblenden C#-Quelltext
1:
2:
Z_wert=Z_wert-5;
        Z.Location= new System.Drawing.Point(Z_wert, 50);

damit eine Picturebox neu gezeichnet wird. Gibt es vielleicht einen Befehl der alles neu zeichnet?


Das Programm mit dem Weiterbewegen der Grafiken nach einem Timerreignis funktioniert auch. Ist schneller ruckelt aber ebenfalls. Als ob verschiedene Windowsereignisse ständig dazwischen kommen.

Egal - für die ersten 2 Tage C# bin ich schon zufrieden. Eine Woche habe ich noch Zeit.

Moderiert von user profile iconKha: Highlight- durch C#-Tags ersetzt
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Sa 03.04.10 23:37 
Es muss "public void" heißen. Wie gesagt, ich habe hier keinen Comipiler installiert. Ist gerade nicht mein Rechner. Daher kann ich nicht debuggen. Ich saug mir den Code praktisch aus den Fingern. Da können sich schon mal Fehler einschleichen. Zudem kann es sein, dass du noch die using-Direktiven hinzufügen musst. Mache dazu mal ein Rechtklick im Code z.B. auf das Wort "Graphics" -> "Auflösen" -> "Using-Direktive hinzufügen". Alle Klassenbezeichnungen müssen Grün sein, dann passt alles.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public void DrawKugel(int ID, int X, int Y)
{
   using(Graphics g = this.CreateGraphics())
   {
       g.DrawImage(meineKugeln[ID].Shape, X, Y);
   }
}


Eine Funktion muss immer einen Rückgabewert besitzen. Wenn sie keinen hat, muss sie als "void" gekennzeichnet sein.

Zitat:

soll die Entwicklingsumgebung eine neue Klasse erstellen oder schreibe ich den Code einfach in "Form1.cs"?


Ich würde dir empfehlen der Übersichlichkeit wegen eine neue cs-Datei hinzuzufügen (rechtsklick auf dein Projekt im Projektbaum -> Hinzufügen -> neue Klasse). Dort schreibst du die Klassen rein. Im Prinzip wäre es egal, aber es ist immer gut, sich von Anfang an einen übersichtlichen Stil anzugewöhnen.


Zitat:
Das Programm mit dem Weiterbewegen der Grafiken nach einem Timerreignis funktioniert auch. Ist schneller ruckelt aber ebenfalls. Als ob verschiedene Windowsereignisse ständig dazwischen kommen.


Windows-Ereignisse stören weniger. Um das ruckeln zu beseitigen, nutzt man sogenannte BufferedGraphic. Diese rendern in den RAM und zeichnen das fertige Ergebnis dann.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
private BufferedGraphicsContext context = BufferedGraphicsManager.Current;

public void DrawKugel(int ID, int X, int Y)
{
   
   context.MaximumBuffer = New Size(this.Width + 1this.Height + 1)


   using(BufferGraphics bg = context.Allocate(this.CreateGraphics(), 
            New Rectangle(00this.Width, this.Height))
   {
      {
          bg.Graphics.DrawImage(meineKugeln[ID].Shape, X, Y);
          bg.Render();
      }
   }
}