Entwickler-Ecke
WinForms - Objekt soll entlang einer Linie gesteuert werden
dubstep - So 11.07.10 21:09
Titel: Objekt soll entlang einer Linie gesteuert werden
Folgendes Problem:
Mein Ziel ist es, dass der User mithilfe einer Steuerung (z.B. Pfeiltasten am Computer) ein Objekt entlang einer Linie bewegen kann.
Ich habe mir diesbezüglich auch schon überlegt, wie man dies angehen könnte, bin mir aber nicht sicher, ob sich dies so umsetzen lässt, wie ich mir das vorstelle?
Die Umsetzung im Detail:
- Bild mit einer schwarzen Linie (die Linie teilt sich dann in zwei Linien auf) mit einem ganz normalen Zeichenprogrogramm erstellen
- Dieses Bild dann in "Form xyz" als BackgroundImage importieren
- Weiteres Bild als PictureBox in "Form xyz" importieren - Dies soll dann das Objekt darstellen, welches entlang dieser schwarzen Linie gesteuert werden kann
- Begrenze Bereiche/Flächen für das Objekt - erreicht wird dies durch Definition von "zugänglichen" und "gesperrten" Pixel in "Form xyz". Nur entlang dieser als "erlaubt" definieren Pixel darf sich das Objekt bewegen. Ich würde alle Pixel, auf dieser schwarzen Linie als "erlaubt" kennzeichnen.
Die Idee mit den Pixeln ist so ziemlich die Einzige, die mir derzeit als Lösungsansatz einfällt - das könnte aber sehr, sehr viel Arbeit werden - geht das auch einfacher?
Wie kann man das mit der Abzweigung (schwarze Linie teilt sich in zwei schwarze Linien auf) realisieren? Die Möglichkeit, dass an dieser Abzweigung eine MessageBox mit einem Text "Wollen Sie rechts oder links fahren?" erscheint, ist ein wenig mühsam.
In weiterer Folge wäre auch eine Art "Stop-Button" ganz nett - mit diesem wird das Objekt gestoppt.
Ich hoffe ich liege mit meinen Lösungsansätzen nicht komplett daneben. :?
Christoph1972 - Mo 12.07.10 07:32
Hallo,
ich denke die Linie solltest du mit GDI+ zeichnen und nicht mit einem Grafikprogramm. So würdest du schon mal die Koordinaten der Line kennen, die dann die Begrenzung darstellen und bei der Bewegung zu überwachen sind.
Yogu - Mo 12.07.10 08:18
Hallo,
wie
Christoph1972 schon gesagt hat, eine Vektorgrafik wäre einfacher zu verwenden. Ist das in der Aufgabenstellung erlaubt?
Wenn nicht, könntest du versuchen, die Bitmap zu vektorisieren. Das ist zwar eigentlich ein sehr komplexes Thema, aber wenn es nur um gerade Linien geht, könnte man es vielleicht vereinfachen. Du könntest die Bitmap in z. B. 4x4-große Bereiche aufteilen, und dann schauen, wie viele Punkte darin schwarz sind. So kannst du vielleicht herausfinden, ob in dem jeweiligen Bereich nichts, eine Linie oder ein Eckpunkt ist. Am Ende verbindest du einfach mal auf Glück alle Eckpunkte.
Grüße,
Yogu
dubstep - Mo 12.07.10 11:01
Danke! Das ist genau die Art von Vorschlag die ich gebraucht habe.
Ich hatte keine Ahnung, dass es soetwas wie GDI+ gibt - derzeit kann ich immerhin schon einmal einen Kreis implementieren.
Christoph1972 hat folgendes geschrieben : |
Hallo,
So würdest du schon mal die Koordinaten der Line kennen, die dann die Begrenzung darstellen und bei der Bewegung zu überwachen sind. |
Das stimmt - nun sind die Koordinaten bekannt. Dank dieser nun gewonnenen Information könnte man doch der Picture Box sagen: "Wenn User klickt "Objekt nach rechts oben", dann bewege dich nach Pixel x/y - dazu bewege dich über Pixel x1/y1, x2/y2, ...
Das praktische ist, dass ich nicht vorhabe, abgerundete Linien oder gar Kreise darzustellen. Es soll lediglich horizontal und vertikale Linien geben, welche miteinander verbunden werden oder sich aufteilen ...
Yogu hat folgendes geschrieben : |
Hallo,
Ist das in der Aufgabenstellung erlaubt? |
Es sind mir keine Beschränkungen vorgegeben.
dubstep - Di 13.07.10 01:33
Bitte um Entschuldigung für das Doppelpost!
Ich habe jetzt als Test eine Linie mit zwei 90°-Ecken erzeugt. Weiters habe ich ein Bild in eine erstellte PictureBox importiert - dieses Bild stellt mein Objekt dar. Zusätzlich habe ich zwei Buttons implementiert. Klickt man auf jeweils einen der zwei Buttons - so springt das Objekt von "Station1" ( = Linienanfang) zu "Station2" ( = Linienende).
Hier einmal der Code:
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:
| protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe) { base.OnPaint(pe); System.Drawing.Graphics g = pe.Graphics; System.Drawing.Pen p1 = new System.Drawing.Pen(System.Drawing.Color.Red);
System.Drawing.Point point1 = new System.Drawing.Point(); point1.X = 120; point1.Y = 100; System.Drawing.Point point2 = new System.Drawing.Point(); point2.X = 100; point2.Y = 100; System.Drawing.Point point3 = new System.Drawing.Point(); point3.X = 120; point3.Y = 120; System.Drawing.Point point4 = new System.Drawing.Point(); point4.X = 140; point4.Y = 120;
g.DrawLine(p1, point1, point2); g.DrawLine(p1, point1, point3); g.DrawLine(p1, point3, point4); }
private void buttonStation1Station2_Click(object sender, EventArgs e) { this.pictureBox1.Left = 140; this.pictureBox1.Top = 120; }
private void buttonStation2Station1_Click(object sender, EventArgs e) { this.pictureBox1.Left = 100; this.pictureBox1.Top = 100; } } } |
Ich habe zwar schon nach Lösungen gesucht und versucht selbständig darauf zu kommen - aber ich werde nicht fündig. Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben?
jaenicke - Di 13.07.10 06:15
dubstep hat folgendes geschrieben : |
Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? |
Du kannst zum Beispiel einen Timer verwenden und dann nach den jeweils x Millisekunden die PictureBox um einen gewissen Wert verschieben.
dubstep hat folgendes geschrieben : |
Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben? |
Direkt nicht, aber du kannst dir ja eine Methode schreiben SetPictureBoxPos, der du die PictureBox und die Koordinaten übergibst. Dann kannst du alles weitere dort berechnen.
Zudem kannst du die PictureBox selbst erweitern, evtl. per partial class, ich weiß nicht ob das bei der Klasse geht.
Yogu - Di 13.07.10 18:26
dubstep hat folgendes geschrieben : |
Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? |
Ein kleines Kochrezept:
- Ermittle die x- und y-Komponenten der Strecke von Punkt A zu Punkt B.
- Berechne die Länge der Strecke mit dem Pythagoras.
- Ermittle die Dauer der Animation, indem du die Länge aus Schritt 2 durch eine Geschwindigkeitskonstante teilst (herauskommen soll eine Anzahl von Zeitabschnitten, z.B. Fünfundzwanzigstelsekunden)
- Bewege das Objekt jeden Zeitabschnitt um die x- und y-Komponente geteilt durch die Anzahl der Zeitabschnitte aus Schritt 3 (ob mit einem Timer, einer Endlosschleife und Application.DoEvents(); oder einer Endlosschleife in einem zweiten Thread und Invoke, bleibt dir überlassen).
dubstep hat folgendes geschrieben : |
Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben? |
Suchst du eine Möglichkeit, einer Komponente einen
System.Drawing.Point zuzuweisen? Schau mal nach
Location :)
Grüße,
Yogu
dubstep - Di 13.07.10 20:51
Hmm, gar nicht so einfach. :lol:
Habe jetzt einmal einen Timer initialisiert. Die Schwierigkeit dabei ist, dies jetzt mit der PictureBox zu verknüpfen. So stelle ich mir das vor: Eine Schleife, die so lange läuft, bis die vorgegebene x-/y-Koordinate von der PictureBox erreicht wurde. In der Schleife selbst steht drinnen: Bewege die Picture Box nach/bei jedem Intervall um n-Pixel. Ich habe zwar schon den Beitrag von "Timer Class" auf "msdn.." gelesen (inkl. Bsp.), aber so wirklich viel mehr Information, als ich ohnehin schon in meinem Code habe, steht hier nicht drinnen. Funktioniert dies so? > Man hat einen Timer mit gewissen Parametern (z.B. Interval, ...), die der User vorgibt. Der User kann dann selbst beliebig viele Events hinzufügen - ein Event stellt dabei eine Art Bedingung dar - also z.B. nach n-Sekunden rufe Event-xyz ab. Im Event steht dann, welcher Vorgang stattfinden soll?
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| private void Form1_Load(object sender, EventArgs e) { System.Windows.Forms.Timer formsTimer = new System.Windows.Forms.Timer(); formsTimer.Interval = 1000; formsTimer.Enabled = true; formsTimer.Tick += new EventHandler(TimerEvent); }
private void TimerEvent(Object myObject, EventArgs myEventArgs) { this.Text = DateTime.Now.ToString(); } |
Yogu hat folgendes geschrieben : |
Suchst du eine Möglichkeit, einer Komponente einen System.Drawing.Point zuzuweisen? Schau mal nach [cs]Location |
Danke :wink: - das funktioniert nun:
this.pictureBox1.Location = new System.Drawing.Point(100, 100);
jaenicke - Di 13.07.10 20:53
dubstep hat folgendes geschrieben : |
Im Event steht dann, welcher Vorgang stattfinden soll? |
Das programmierst du dort, ja. In Abhängigkeit von der verstrichenen Zeit kannst du ausrechnen wo sich deine Box dann befindet.
Eine Schleife ist das dann nicht, sondern einfach alle x Millisekunden ein ausgeführter Code.
dubstep - Di 13.07.10 21:11
Wäre dies eine Möglichkeit? Man sagt: Rufe nach 3000ms EventHandler "TimerEvent" ab.
C#-Quelltext
1: 2: 3: 4:
| if (formsTimer.Interval.Equals(3000)) { formsTimer.Tick += new EventHandler(TimerEvent); } |
jaenicke - Di 13.07.10 21:15
Nein, der EventHandler wird automatisch im eingestellten Intervall immer wieder aufgerufen. ;-)
Also zum Beispiel alle 3000 Millisekunden.
dubstep - Di 13.07.10 21:23
jaenicke hat folgendes geschrieben : |
Nein, der EventHandler wird automatisch im eingestellten Intervall immer wieder aufgerufen. ;-) |
Achso - dann wird alleine durch "formsTimer.Interval = 1000;" von mir vorgegeben wie oft ein Event aufgerufen werden soll. Kann es sein, dass dies dann ein wenig unflexibel ist? Habe ich zum Beispiel zwei- oder mehrere Events, dann werden die alle im Intervall von 1000ms neu aufgerufen? Wenn man nun ein zweites Event hat, welches z.B. nur alle 5000ms (und nicht wie Event1 alle 1000ms) neu aufgerufen werden soll, dann benötigt man also einen weiteren Timer, da ja das Intervall nur einmal pro Timer eingestellt werden kann?
jaenicke - Di 13.07.10 21:28
dubstep hat folgendes geschrieben : |
Wenn man nun ein zweites Event hat, welches z.B. nur alle 5000ms (und nicht wie Event1 alle 1000ms) neu aufgerufen werden soll, dann benötigt man also einen weiteren Timer, da ja das Intervall nur einmal pro Timer eingestellt werden kann? |
Nein, dann führst du die Aktion eben nur bei jedem fünften Timeraufruf, eben wenn 5 Sekunden vorbei sind, durch. ;-)
dubstep - Di 13.07.10 21:48
jaenicke hat folgendes geschrieben : |
Nein, dann führst du die Aktion eben nur bei jedem fünften Timeraufruf, eben wenn 5 Sekunden vorbei sind, durch. ;-) |
Stimmt - das klingt logisch. :lol:
Gibt es einen Grund, warum sich die PictureBox nicht bewegt? Eigentlich sollte sie nach 5000ms zu 300/100 "springen" und ab dann dort verharren.
Gibt es eine Möglichkeit, dieses Event dann zu stoppen, weil das Ziel - die PictureBox nach 300/100 zu bewegen ist ja dann (wenn es funktioniert) erreicht - somit ist jeder weitere Aufruf dieses Events unnötig?!
C#-Quelltext
1: 2: 3: 4:
| private void TimerEvent(Object myObject, EventArgs myEventArgs) { this.pictureBox1.Location = new System.Drawing.Point(300, 100); } |
Edit: Wobei ich gerade sehe, dass ich hier einen Denkfehler habe. Es ergibt ja gar keinen Sinn, dass ich fixe Koordinatenpunkte angebe - dazu würde ich ja keinen Timer benötigen. Sinnvoller wäre es, im Event zu definieren, um wie viele Pixel sich die PictureBox bewegen soll - User "Yogu" hat bereits beschrieben wie hier vorgegangen werden muss.
jaenicke - Di 13.07.10 21:55
Ein kleines Beispiel im Anhang, das macht es vielleicht klarer. ;-)
Die Berechnung sieht bei dir dann natürlich anders aus, aber da bewegt sich schonmal was. :mrgreen:
Yogu - Di 13.07.10 22:05
dubstep hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | Nein, dann führst du die Aktion eben nur bei jedem fünften Timeraufruf, eben wenn 5 Sekunden vorbei sind, durch. ;-) |
Stimmt - das klingt logisch. :lol: |
Oder du erstellst einfach einen zweiten Timer. :idea:
dubstep hat folgendes geschrieben : |
Gibt es einen Grund, warum sich die PictureBox nicht bewegt? Eigentlich sollte sie nach 5000ms zu 300/100 "springen" und ab dann dort verharren.
Gibt es eine Möglichkeit, dieses Event dann zu stoppen |
Ja - setze einfach die Eigenschaft
Enabled auf
false, und der Event-Handler wird nicht mehr aufgerufen.
dubstep hat folgendes geschrieben : |
User "Yogu" hat bereits beschrieben wie hier vorgegangen werden muss. |
Hast du das soweit verstanden? Schon umgesetzt?
dubstep - Di 13.07.10 22:12
jaenicke hat folgendes geschrieben : |
Ein kleines Beispiel im Anhang, das macht es vielleicht klarer. ;-)
Die Berechnung sieht bei dir dann natürlich anders aus, aber da bewegt sich schonmal was. :mrgreen: |
Danke - dein Beispiel hat einiges viel klarer gemacht. Ich habe nicht gewusst, dass man einfach einen Timer aus Toolbox/Komponenten per Drag&Drop in die Form ziehen kann - das erspart viel Arbeit! Zumindest bewegt sich die PictureBox schon mal. :lol:
Yogu hat folgendes geschrieben : |
Hast du das soweit verstanden? Schon umgesetzt? |
Vorgehensweise ist klar - umgesetzt ist es noch nicht - ich hoffe das wird noch.
dubstep - Di 13.07.10 22:40
Yogu hat folgendes geschrieben : |
Hast du das soweit verstanden? Schon umgesetzt? |
Ich habe nun z.B. zwei Punkte:
Punkt 1: x1=100/y1=100
Punkt 2: x2=200/y2=200
Satz Pythagoras:
Wurzel((x1-x2)² + (y1-y2)²)
Länge zwischen Punkt 1 und Punkt 2 = 141,4 Pixel
Geschwindigkeitskonstante: 20,20 (frei gewählter Wert)
Dauer der Animation = Länge/Geschwindigkeitskonstante = 7,00 Sekunden
7,00 Sekunden um 141,4 Pixel zu überwinden
Event wird alle 1000ms aufgerufen >> 1000ms = 1 Sekunde
141,4 Pixel / 7,00 Sekunden = 20,2 Pixel/Sekunde = Überwindung von 20,2 Pixel pro Event
Ich hoffe das passt nun - aber was fange ich jetzt mit der gewonnenen Information an?
______________________________
C#-Quelltext
1: 2: 3: 4: 5: 6: 7:
| private void timer1_Tick(Object myObject, EventArgs myEventArgs) { if (pictureBox1.Location == new System.Drawing.Point(100, 81)) { pictureBox1.Hide(); } else {pictureBox1.Left = pictureBox1.Left + 1;} } |
Die PictureBox bewegt sich zwar jetzt brav entlang der mit GDI+ gezeichneten Linie, aber wie bringe ich sie an einem bestimmten Koordinatenpunkt wieder zum stoppen? Da ich keinen Befehl zum stoppen gefunden habe, habe ich es mit "hide" versucht - aber auch hiervon lässt sich die PictureBox nicht beeindrucken. Danach habe ich es noch mit diesem Befehl probiert, aber auch dies war leider erfolglos:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7:
| private void timer1_Tick(Object myObject, EventArgs myEventArgs) { if (pictureBox1.Location == new System.Drawing.Point(100, 81)) { pictureBox1.Left = pictureBox1.Left + 0;} else {pictureBox1.Left = pictureBox1.Left + 1;} } |
Obwohl er prinzipiell über die if-else-Anweisung geht, sonst würde er sich ja nicht andauernd für die else-Anweisung entscheiden. Setzt man "pictureBox1.Left = pictureBox1.Left + 0" bei else ein, dann bewegt sich die PictureBox erst gar nicht, deswegen habe ich dies statt dem Stop-Befehl bei der if-Anweisung versucht.
Edit: Mit "(pictureBox1.Location == new System.Drawing.Point(100,
84))" funktioniert es jetzt! Liegt vielleicht daran, weil sich die PictureBox entlang der Pixel (y kleiner als 81) entlang der x-Achse bewegt.
Yogu - Mi 14.07.10 16:13
dubstep hat folgendes geschrieben : |
141,4 Pixel / 7,00 Sekunden = 20,2 Pixel/Sekunde = Überwindung von 20,2 Pixel pro Event |
Stimmt zwar auch, ist aber nicht, das was du brauchst. Schau mal, was ich geschrieben habe:
Yogu hat folgendes geschrieben : |
4. Bewege das Objekt jeden Zeitabschnitt um die x- und y-Komponente geteilt durch die Anzahl der Zeitabschnitte aus Schritt 3.[/list] |
Du musst nicht die Strecke von 141,4 Pixel durch die Zeit teilen, sondern (x1-x2) und (y1-y2). In jedem Intervall addierst du nun zur aktuellen Position
new Point((x1-x2) / 7, (y1-y2) / 7)) (die 7 muss natürlich durch eine Variable ersetzt werden.
Das Problem bei dieser Variante ist, dass das Objekt nicht unbedingt genau dort ankommt, wo es laden soll, weil ja jedes Mal gerundet werden muss. Abhilfe verschaffen könnten zwei
float-Variablen, die aktuelle Position als Kommazahl speichern, damit nichts verloren geht.
dubstep hat folgendes geschrieben : |
Die PictureBox bewegt sich zwar jetzt brav entlang der mit GDI+ gezeichneten Linie, aber wie bringe ich sie an einem bestimmten Koordinatenpunkt wieder zum stoppen? |
Du zählst einfach bei jedem Event-Handler eine Variable noch, und wenn die die von dir ermittelte Dauer der Animation erreicht hat, schaltest du den Timer einfach per
timer.Enabled = false; aus.
Kha - Mi 14.07.10 20:59
Wenn man in Vektoren fit ist (was sich bei Grafikprogrammierung eindeutig anbietet), lässt sich Yogus Vorschlag auch so zusammenfassen:
Soll sich ein Objekt geradlinig von
a nach
b mit der konstanten Geschwindigkeit
v bewegen, gilt für seine Position
C#-Quelltext
1:
| p(t) = a + vt/|b-a|*(b-a) |
dubstep - Do 15.07.10 00:44
Ich habe jetzt mal folgendes initialisiert:
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:
| private void timer1_Tick(Object myObject, EventArgs myEventArgs) { if (pictureBox1.Location == new System.Drawing.Point(185, 100)) { Thread.Sleep(1000); pictureBox1.Left = pictureBox1.Left + 1; }
else if(pictureBox1.Location == new System.Drawing.Point(335, 100)) { Thread.Sleep(1000); pictureBox1.Left = pictureBox1.Left + 1; }
else if (pictureBox1.Location == new System.Drawing.Point(400, 100)) { for (int i = 1; i <= 36; i++) { pictureBox1.Top = pictureBox1.Top - 1; pictureBox1.Left = pictureBox1.Left + 1; } }
else if (pictureBox1.Location == new System.Drawing.Point(550, 80)) { Thread.Sleep(1000); timer1.Enabled = false; }
else { pictureBox1.Left = pictureBox1.Left + 1; } } } } |
Ein Objekt, welches an 3 Punkten halten sollte. Dies macht es aber nicht. Das Objekt startet und fährt bis zum Punkt 185/100 - dort wartet es 1 Sekunde - dann fährt es weiter bis zu einem zweiten Haltepunkt - nach diesem wechselt es auf eine andere mit GDI+ gezeichnete Linie. Das funktioniert soweit. Danach sollte es bei x=550/y=80 stoppen und der Timer enden - dies funktioniert aber nicht - das Objekt fährt ganz normal weiter. Ich habe schon so ziemlich alle y-Werte (auch die, die eigentlich gar nicht möglich sein dürften) ausprobiert, aber die PictureBox fühlt sich dadurch nicht angesprochen.
Weiters hätte ich gerne, dass die PictureBox an jedem Haltepunkt für die Dauer des Haltes das Bild wechselt. Nach dem Halt - beim Anfahren, soll das Bild wieder in den ursprünglichen Zustand gehen. Die gif-Animation existiert schon, aber es gibt ein anderes Problem:
C#-Quelltext
1: 2:
| FileStream imageStream = new FileStream("C:\\Users\\vista\\xxx.gif", FileMode.Open, FileAccess.Read); pictureBox1.Image = System.Drawing.Image.FromStream(imageStream); |
Wenn ich das Bild in einer PictureBox während eines Haltes ersetzen will, dann wird die gif-Animation nicht abgespielt - das resultiert denke ich daraus, weil während des Haltes ein "Thread.Sleep(1000);" existiert. Kann man dies irgendwie umgehen?
lg
jaenicke - Do 15.07.10 04:18
dubstep hat folgendes geschrieben : |
Danach sollte es bei x=550/y=80 stoppen und der Timer enden - dies funktioniert aber nicht - das Objekt fährt ganz normal weiter. Ich habe schon so ziemlich alle y-Werte (auch die, die eigentlich gar nicht möglich sein dürften) ausprobiert, aber die PictureBox fühlt sich dadurch nicht angesprochen. |
Wozu gibt es eigentlich den Debugger? :zwinker:
Na gut, nehmen wir mal den Brain Debugger: :mrgreen:
Also, nachdem du bei (400, 100) warst gehst du in einer Schleife auf (436, 64). Dann wartest du auf (550, 80), die PictureBox ist aber 16 Pixel weiter oben. ;-)
dubstep hat folgendes geschrieben : |
das resultiert denke ich daraus, weil während des Haltes ein "Thread.Sleep(1000);" existiert. Kann man dies irgendwie umgehen? |
Ja, kein Sleep benutzen, das hat da nix zu suchen...
Du hast einen Timer um gerade keine Schleifen oder Wartezeiten zu benötigen. Du machst einfach alles was zu diesem Zeitpunkt gemacht werden muss. Mehr nicht. Und wenn du eine Sekunde nichts machen willst, dann darf dein Timer-Eventhandler eben eine Sekunde lang nichts machen.
Sprich du kannst eine Variable mit der Anfangszeit des Haltes setzen und erst nach Ablauf der Sekunde führst du in deinem Event wieder etwas aus.
dubstep - Do 15.07.10 07:19
jaenicke hat folgendes geschrieben : |
Also, nachdem du bei (400, 100) warst gehst du in einer Schleife auf (436, 64). Dann wartest du auf (550, 80), die PictureBox ist aber 16 Pixel weiter oben. ;-) |
Ja, das wäre zwar logisch und nachvollziehbar, aber es funktioniert trotzdem nicht. Ich habe dann noch zur Sicherheit die Werte 50, 55, 60, 65, 70, 75, 80 eingesetzt, aber die PictureBox wird durch keine dieser eingesetzten y-Werte angesprochen. Beim Debugging (bin nicht allzu geübt darin), glaube ich, dass der Debugger erst gar nicht in diese eine else-if-Anweisung reingeht - alle anderen else-if-Anweisungen inkl. der else-Anweisung werden aber "benutzt".
jaenicke hat folgendes geschrieben : |
Sprich du kannst eine Variable mit der Anfangszeit des Haltes setzen und erst nach Ablauf der Sekunde führst du in deinem Event wieder etwas aus. |
Das ist eine sehr gute Idee! Ich habe nun in alle else-if-Anweisungen das "Thread.Sleep" durch "timer1.Interval = 1000;" ersetzt. Die else-Anweisung hat ein "timer1.Interval = 25;" erhalten. Durch "timer1.Interval" ist es mir ja jetzt sogar möglich, Geschwindigkeiten abschnittsweise zu regeln. :shock: :D
Angenommen man hat zwei ComboBoxen. In einer ComboBox gibt der User den Startpunkt (z.B. eine Station) der PictureBox bekannt, in der anderen ComboBox wird der Zielort (ebenfalls eine Station) der ComboBox bestimmt.
Das ganze würde dann so aussehen:
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:
| private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { if (comboBox1.SelectedItem.ToString() == "Haltepunkt1") { ...? } else if (comboBox1.SelectedItem.ToString() == "Haltepunkt2") { ...? } else if (comboBox1.SelectedItem.ToString() == "Haltepunkt3") { ...? } private void comboBox2_SelectedIndexChanged(object sender, EventArgs e) { if (comboBox2.SelectedItem.ToString() == "Haltepunkt1") { ...? } else if (comboBox2.SelectedItem.ToString() == "Haltepunkt2") { ...? } else if (comboBox2.SelectedItem.ToString() == "Haltepunkt3") { ...? } |
Doch habt ihr eine Idee, wie man das nun in verwertbare Information konvertieren könnte? Also z.B. die Information aus ComboBox1 und ComboBox2 kombinieren und daraus einen Schluss ziehen. Es wäre dann ganz nett, wenn der User eingibt, dass die PictureBox von Haltepunkt1 nach Haltepunkt3 fahren soll, dass diese dann das auch macht. Ich hoffe das ganze wird dann nicht all zu umfangreich. :? Aber vielleicht ist eine ComboBox das falsche Medium dazu? Wäre eine Textbox in die der User selbst seine gewünschte Route eingibt besser? Hier hätte man nämlich gleich beide Informationen (Startort und Zielort) in einem Medium vereint. Dann könnte man mit einer if-else-Abfrage die nötigen Schlüsse aus der gewonnenen Information ziehen.
dubstep - Do 15.07.10 23:20
Im weiteren Verlauf habe ich nun noch einen "interne Zeitanzeige" hinzugefügt. Diese soll die Uhrzeit in Stunden und Minuten im Zeitraffer anzeigen. Dabei bin ich gleich auf mehrere Probleme gestoßen. Das mit Abstand größte Problem ist die Performance. Hier mal der Code:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| private void timer1_Tick(object sender, EventArgs e) { for (int j = 0; j < 1441; j++) { for (int i = 0; i < j; i++) {
int value1 = 1 + i; double value2 = 1 + i; int time = value1 / 60; double time2 = (value2 / 60); double time3 = ((time2 - time) * 100); double time4 = (time3 * 0.6); string time5 = String.Format("{0:F0}", time4);
textBox1.Text = time.ToString(); textBox2.Text = time5.ToString(); } textBox1.Update(); textBox2.Update(); } } |
Dabei werden sowohl die Stunden als auch die Minuten in einer eigenen TextBox ausgegeben. Ich vermute, dass das hohe "i" von 1441 die Performanceprobleme verursacht. Mein mit einem zweiten Timer gesteuertes Objekt erreicht kaum mehr eine Geschwindigkeit - selbst bei Interval=10.
Warum überhaupt eine interne Zeit? > Zukünftig habe ich vor, das Objekt zu einer bestimmten Zeit (z.B. 20:22h) starten zu lassen. Dies sollte in Zeitraffer (1 Tag = ~10 Minuten) passieren.
Das zweite Problem (ebenfalls Performance): Die Uhrzeit vergeht anfangs sehr schnell, doch mit vortschreitender Uhrzeit (= hohes "i") dauert eine Stunde immer länger. Kann man hier nicht eine halbwegs (zumindest für das Auge) konstante Geschwindigkeit halten?
Danke schon mal!
jaenicke - Do 15.07.10 23:59
dubstep hat folgendes geschrieben : |
selbst bei Interval=10 |
Selbst wenn so ein kleines Intervall möglich
wäre, wie soll denn ein Mensch 1000 / 10 = 100 Aktualisierungen pro Sekunde sehen?
Fazit: Mach das Intervall höher und überspringe stattdessen Zahlen bei der Anzeige.
Außerdem hast du offenbar das Konzept eines Timers immer nicht nicht verstanden, sonst würdest du keine Schleife in das Event schreiben...
Es geht doch gerade darum, dass im Event immer der aktuelle Schritt durchgeführt wird. Ohne Schleife.
dubstep - Fr 16.07.10 00:07
jaenicke hat folgendes geschrieben : |
..., sonst würdest du keine Schleife in das Event schreiben...
Es geht doch gerade darum, dass im Event immer der aktuelle Schritt durchgeführt wird. Ohne Schleife. |
Stimmt - ein Denkfehler von mir - das macht natürlich absolut keinen Sinn! Ich habe den Timer jetzt rausgenommen - es funktioniert zwar - aber die Performanceprobleme gibt es nach wie vor.
Edit:
Habe jetzt alles nocheinmal überdacht und habe nun doch die Entscheidung getroffen, einen Timer zu nehmen - die Performance passt jetzt auch und stellt absolut kein Problem mehr dar! Damit bin ich meinem Ziel schon einmal einen erheblichen Schritt weiter gekommen. :lol: Eine Frage hätte ich jedoch noch dazu: Ist es möglich, dass die Uhrzeit statt in diesem Format:
11:4 oder 3:29 >> in diesem Format: 11:04 oder 03:29 angezeigt wird?
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:
| int time = 0; int erg = 0;
private void timer1_Tick(object sender, EventArgs e) { if (textBox1.Text == "23:59") { timer1.Enabled = true; }
else { int time2 = 1;
erg = time + time2; time = erg;
int basiswert = erg / 60; double basiswert2 = erg % 60;
string basiswert3 = String.Format("{00:F0}:", basiswert); string basiswert4 = String.Format("{00:F0}", basiswert2);
textBox1.Text = basiswert3.ToString() + basiswert4.ToString(); textBox1.Update(); } } |
jaenicke - Fr 16.07.10 06:12
dubstep hat folgendes geschrieben : |
Habe jetzt alles nocheinmal überdacht und habe nun doch die Entscheidung getroffen, einen Timer zu nehmen - die Performance passt jetzt auch |
So war meine Aussage auch gemeint. ;-)
dubstep hat folgendes geschrieben : |
Ist es möglich, dass die Uhrzeit statt in diesem Format:
11:4 oder 3:29 >> in diesem Format: 11:04 oder 03:29 angezeigt wird? |
Ja.
Willst du auch wissen wie? ;-)
C#-Quelltext
1: 2:
| string basiswert3 = String.Format("{0:00}:", basiswert); string basiswert4 = String.Format("{0:00}", basiswert2); |
dubstep - Fr 16.07.10 16:55
Die PictureBox erscheint nun um z.B. 00:10 Uhr (interne Zeit) bei Pixel 775/527 und bewegt sich entlang der x-Achse in Richtung links. Ab einem bestimmten Punkt auf der x-Achse, soll sich die PictureBox aber entlang der y-Achse nach oben bewegen.
Im Prinzip soll sich die PictureBox ja nur bei y = 527 nach links bewegen. Gibt es eine Möglichkeit statt nur einem x-Wert gleich ein ganzes Spektrum an x-Werten anzugeben? Also ich bräuchte z.B. von x=775 bis x=620 oder aber von x= +unendlich bis x=620 ginge auch. Gibt es eine Möglichkeit dies so anzugeben (statt dem xxx eben dieses Spektrum an x-Werten)?
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| private void timer01_Tick(object sender, EventArgs e) { if (textBox1.Text == "00:10") { pictureBox01.Visible = true; pictureBox01.Location = new System.Drawing.Point(775, 527); }
else if (pictureBox01.Location == new System.Drawing.Point(xxx, 527)) { time01.Interval = 100; pictureBox01.Left = pictureBox01.Left - 1; } else { timer01.Interval = 100; pictureBox01.Top = pictureBox01.Top - 1; } |
jaenicke - Fr 16.07.10 19:31
Wie wäre es, wenn du dir die beiden Werte der Location getrennt anschaust? ;-)
Es gibt in der Mathematik durchaus auch "größer als" und sowas, genauso gibt es das auch in C# als Operatoren. Denn du willst ja wissen, ob der Wert größer als ein bestimmter und kleiner als ein anderer ist (zum Beispiel).
dubstep - Fr 16.07.10 20:11
DANKE vielmals! :D
C#-Quelltext
1: 2: 3: 4: 5:
| else if ((pictureBox01.Location.X >= 622) && (pictureBox01.Location.Y == 527)) { timer01.Interval = 100; pictureBox01.Left = pictureBox01.Left - 1; } |
Edit:
Ist es ratsam, wenn sich zwei PictureBoxen gleichzeitig bewegen sollen, einen zweiten Timer zu nehmen? Ich habe versucht beide PictureBoxen durch einen Timer bewegen zu lassen, aber so wirklich gut funktioniert das nicht.
dubstep - Mo 19.07.10 14:25
Hmm - hat denn niemand eine Idee? :(
Gibt es eine Möglichkeit 3 Timer gleichzeitig laufen zu lassen (1 Timer für die interne Zeit, ein zweiter Timer, welcher dafür sorgt, dass sich das Objekt bewegt (mit 24 if-else-Abfragen :? ) und ein dritter Timer, welcher genau die selbe Aufgabe wie der zweite Timer nur für ein weiteres Objekt erfüllt.
Ich weiß jetzt nicht ob dass die Grenzen von GUI sprengt, aber mein Ziel wäre es, rund 50 Objekte oder PictureBoxen gleichzeitig bewegen zu lassen. Gibt es da vielleicht einen Ansatz, welcher die Performance nicht so sehr belastet?
Kha - Mo 19.07.10 18:35
dubstep hat folgendes geschrieben : |
Ich habe versucht beide PictureBoxen durch einen Timer bewegen zu lassen, aber so wirklich gut funktioniert das nicht. |
Dann lass es uns verbessern ;) . Es genügt auf jeden Fall ein einziger Timer und wenn du die Punkte geschickt in eine Liste ablegst, wirst du auch keine 24
ifs benötigen.
dubstep - Mo 19.07.10 21:03
Ich hoffe das reicht so: =)
Im Anhang findet ihr die Datei - falls die zwei Bilder nicht angezeigt werden - ich habe beide ebenfalls in die Projektmappe geladen. Diese findet ihr im Projektmappen-Explorer. Ein Bild ist für den Hintergrund der Form, das andere Bild für die PictureBox.
Vielen Dank nochmal und bitte nicht über das Projekt wundern. :wink:
Kha - Mo 19.07.10 22:54
Das war vielleicht die falsche Wortwahl :) . Ich dachte eher daran, dass du uns vorstellst, was "nicht so wirklich gut" funktioniert, und es dann mit Hinweisen von uns selbst lösen kannst. Das ist jedenfalls grob der Gedanke hinter diesem Forum ;) .
dubstep - Mi 21.07.10 00:43
Kha hat folgendes geschrieben : |
..., was "nicht so wirklich gut" funktioniert, und es dann mit Hinweisen von uns selbst lösen kannst. Das ist jedenfalls grob der Gedanke hinter diesem Forum ;) . |
Am Versuch nur einen Timer für beide PictureBoxen zu verwenden bin ich leider gescheitert. Dieser Fehler erscheint:
"Der Typ "WindowsFormsApplication.FormSimulation" definiert bereits einen Member namens "timer1_Tick" mit den gleichen Parametertypen."
Mir ist zwar klar was das nun heißt (bei Timer1 kann man unter Ereignis zwischen zwei Verhalten - beide nennen sich Timer1_Tick - wählen, es darf aber nur ein Verhalten ausgewählt werden), aber wie kann man das Problem lösen?
Ein Lösungsansatz wäre, dass die Bewegung in Abhängigkeit vom Timer durch zwei verschiedene Ereignisse (z.B. einmal PictureBox_Click und einmal timer1_Tick) ausgelöst wird, aber das will ich nicht. Bei beiden Pictrue-Boxen soll die Bewegung durch das selbe Ereignis ausgelöst werden.
Durch die vorgegebene Oberfläche (wo man einfach nur das Verhalten auswählen braucht) geht es jedenfalls nicht, aber vielleicht gibt es eine Möglichkeit den Code manuell einzugeben und zwar so, dass der Timer beide Verhalten annimmt. Bei den Eigenschaften eines Buttons funktioniert dies ja ähnlich - man kann die Eigenschaften (z.B. Location) über die vorgefertigte Oberfläche auswählen oder man gibt sie manuell mittels Code vor. Bei der manuellen Eingabe hat man selbstverständlich mehr Freiheiten.
Kha hat folgendes geschrieben : |
Es genügt auf jeden Fall ein einziger Timer und wenn du die Punkte geschickt in eine Liste ablegst, wirst du auch keine 24 ifs benötigen. |
Wie ist das mit dieser Liste gemeint - eine LinkedList wird es wohl nicht sein?!
Kha - Mi 21.07.10 13:01
dubstep hat folgendes geschrieben : |
Bei beiden Pictrue-Boxen soll die Bewegung durch das selbe Ereignis ausgelöst werden. |
C#-Quelltext
1: 2: 3: 4: 5:
| void timer1_Tick(...) { CodeUmDieErstePictureBoxZuBewegen(); CodeUmDieZweitePictureBoxZuBewegen(); } |
:gruebel: ?
dubstep hat folgendes geschrieben : |
Wie ist das mit dieser Liste gemeint - eine LinkedList wird es wohl nicht sein?! |
Nun, eine Liste wäre das auch, aber normalerweise würde ich eher zu einer
List<T> oder einem Array tendieren. Wie das nun genau aussehen muss, kann ich nicht sagen, ohne den exakten gewünschten Bewegungsablauf zu kennen.
dubstep - Mi 21.07.10 13:47
Achso - stimmt, so funktioniert das. Ich hatte vorhin zwei Funktionen mit dem selben Namen erstellt, das geht natürlich nicht - keine Ahnung wie ich überhaupt auf solch eine absurde Idee gekommen bin!? Jedenfalls habe ich jetzt daraus eine wichtige Konsequenz gezogen. Ich habe nun 4 Buttons (ohne lange if-else-Abfrage) gleichzeitig bewegen lassen und die Performance wird in keiner weise gstört - das heißt, die Reduktion auf einen Timer bringt (wie du es mir geraten hast) schon einmal Abhilfe. =)
Kha hat folgendes geschrieben : |
... eher zu einer List<T> oder einem Array tendieren. Wie das nun genau aussehen muss, kann ich nicht sagen, ohne den exakten gewünschten Bewegungsablauf zu kennen. |
Mit gewöhnlichen Lists habe ich noch nicht gearbeitet. Ich denke, ich werde es einmal in Form eines Arrays versuchen. Wäre ein Dictionary auch eine mögliche Option?
dubstep - So 08.08.10 17:29
Bezüglich der "Performanceprobleme" habe ich einen tollen Tipp bekommen. :lol:
Zwar mögen die vielen if-else-Abfragen vielleicht hinderlich sein, aber sie sind nicht ausschließlich der Grund für die Probleme. Umgehen lässt sich das alles ganz einfach und zwar mit "Double Buffering":
http://msdn.microsoft.com/en-us/library/ms229622.aspx
Dank "Double Buffering" ist es nun möglich ohne Probleme 20 PictureBoxen zeitgleich in einer Form bewegen zu lassen - trotz der vielen if-else-Abfragen.
Soweit ist dieses Problem mal gelöst. :)
Eine kurze Frage noch: Ich habe eine Textbox. In diese Textbox schreibt der User Zahlen (werden aber trotzdem als string angesehen). Nun habe ich eine zweite Textbox. In dieser soll der Text der ersten Textbox und zusätzlich noch ein immer gleich bleibender Text angezeigt werden.
Das ganze sieht dann zum Beispiel so aus:
Eingabe User Textbox1: "3320"
User klickt auf Button
Ausgabe Textbox2: "Die Zahl ist 3320"
..., wobei "Die Zahl ist" konstant und "3320" je nach Benutzereingabe variabel ist.
C#-Quelltext
1: 2: 3: 4:
| private void button1_Click(object sender, EventArgs e) { textBox2.Text = ("Die Zahl ist {0}", textBox1.Text); } |
Irgendwie hat das mit Console.WriteLine immer so toll geklappt, aber da dies ja hier keinen Sinn mehr hätte ... :(
Ich habe schon so viel probiert ... aber es funktioniert einfach nicht.
Kha - So 08.08.10 17:47
Für neue Fragen, die nichts mehr mit dem alten Thema zu tun haben, bitte einen neuen Thread aufmachen. Vielleicht genügt dir aber schon dieser Wink :) : string.Format.
dubstep - So 08.08.10 18:37
Kha hat folgendes geschrieben : |
Für neue Fragen, die nichts mehr mit dem alten Thema zu tun haben, bitte einen neuen Thread aufmachen. Vielleicht genügt dir aber schon dieser Wink :) : string.Format. |
Danke - hat wunderbar funktioniert. Ich hatte es zuvor nur mit ".toString" und "Convert.ToString" versucht, aber auf "string.Format" wäre ich nicht gekommen.
C#-Quelltext
1:
| textBox2.Text = string.Format("Die Zahl ist {0}", textBox1.Text); |
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!