Entwickler-Ecke

WinForms - Zeichenbereich eines Panels, "Animation" von Kreisen


CodeKiddy - Sa 14.04.18 15:47
Titel: Zeichenbereich eines Panels, "Animation" von Kreisen
Ein freundliches Hallo in die Runde. :D

Ich trete heute mit einem Problem an euch ran, dass mich zum Verzweifeln bringt. Trotz etlichen Momenten des Brainfucks war es mir nicht möglich, dieses Problem zu beheben. :roll: :(
Unzwar soll ich in einer Hausaufgabe eine kleine Anwendung entwerfen, wo man ein bisschen mit dem Zeichenbereich eines Panels "spielen" kann, indem man ein paar Kreise, Rechtecke, Linien hinzeichnet. Das habe ich ziemlich gut hinbekommen. Doch das "Sahnehäubchen" der Anwendung soll sein, eine kleine "Animation" erstellen zu können, wo im Panel ein Kreis oder ein Rechteck von einem Ausgangspunkt in der Mitte des Panels alle paar Sekunden ein wenig weiter wandert, bis der Rand des Panels ungefähr erreicht ist und sich anschließend wieder zur Mitte hin bewegen, bis er seinen ursprünglichen Ausgangspunkt einigermaßen wieder hat. Dabei soll der Anwender die Zahl der Wiederholungen vorgeben.
Ich habe alle nötigen Komponenten erstellt und auch den Code gut vervollständigt, doch ich komme einfach nicht an dem Problem vorbei, wie ich es schaffe, dass die Zeichnung der Kreise/Rechtecke am Rand stoppt und wieder beginnt, die Zeichnungen an innen auszuführen. Der Abstand jeder Zeichnung beträgt 5 Punkte zu seiner Ausgangszeichnung. Bei mir geht die Zeichnung jedoch immer über den Rand des 300 x 300 Punkte großen Panels hinaus. Mir ist schleierhaft, wie ich die Grenzen richtig bestimme. Seltsamerweise habe ich es aber geschafft, den umgekehrten Effekt zu erzielen: vom Ausgangspunkt nach innen zu zeichnen und sich dann wieder zu vergrößern. Das ist aber ja nicht das Ziel, und ich konnte es nicht schaffen, es umzukehren.
Hat einer von euch eine Lösung?

Wie gesagt, das Panel soll eine Größe von 300 x 300 haben. Die Kreise und Rechtecke von 100 x 100 (anfangs, denn der Anwender kann hier zwischen drei Größen wählen, aber ich denke, wenn ich verstehe, wie das mit einer Größe funktioniert, bekomme ich es auch für die anderen hin :D). Der Mittelpunkt aller Figuren liegt bei 150, 150. Veränderung bei jeder Zeichnung um 5 in jede Richtung. Nach jedem Zeichnen soll die Figur nach 100 ms gelöscht werden. Der Anwender gibt die Anzahl der Wiederholungen vor (was mich zu der Überlegung führt, wie ich es nach dem Zurückzeichnen auch noch schaffe, dass es sich WIEDER vergrößert... :?: ). Die Wiederholungsrate kann zwischen 1 und 200 liegen. Den Stift sucht der Anwender sich vorher aus; braucht also bei dem Problem jetzt nicht groß beachtet werden, da er zu dem Zeitpunkt schon festgelegt ist. Startpunkt der ersten Zeichnung ist erst einmal irrelevant; kann ich ja später an mein Programm anpassen.


Uff, ich hoffe, ich habe mich verständlich ausgedrückt und ihr könnt mir helfen. :lol: :oops:



Liebe Grüße, CodeKiddy. :)


Th69 - Sa 14.04.18 16:52

Hallo,

ich denke, es ist einfacher, wenn du uns deinen bisherigen Code für die Animation zeigst. Verwendest du denn die Timer-Komponente?
Das Erkennen des Randes sollte ja nur ein bißchen Mathematik sein.


CodeKiddy - Mo 16.04.18 16:52

Oh, natürlich gerne. :D
Nein, ich habe keinen Timer verwendet, sondern die Methode genommen, die auch in meinem Lernheft verwendet wird.


Ich habe bisher folgendes zusammengebastelt:


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:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
namespace kleineSpielerei
{
    public partial class FormSpielerei : Form
    {
        //für die Zeichenfläche
        Graphics zeichenflaeche;
        //für die Farbe der Linien
        Color linienfarbe;
        //für die Hintergrundfarbe
        Color hintergrundfarbe;

        //Array für den Linienstil
        System.Drawing.Drawing2D.DashStyle[] linienstil =
        {
            System.Drawing.Drawing2D.DashStyle.Dash,
            System.Drawing.Drawing2D.DashStyle.DashDot,
            System.Drawing.Drawing2D.DashStyle.DashDotDot,
            System.Drawing.Drawing2D.DashStyle.Dot,
            System.Drawing.Drawing2D.DashStyle.Solid
        };

        //Array für die Hintergrundmuster
        System.Drawing.Drawing2D.HatchStyle[] fuellstil =
        {
            System.Drawing.Drawing2D.HatchStyle.Cross,
            System.Drawing.Drawing2D.HatchStyle.DottedGrid,
            System.Drawing.Drawing2D.HatchStyle.ForwardDiagonal,
            System.Drawing.Drawing2D.HatchStyle.Sphere,
            System.Drawing.Drawing2D.HatchStyle.Vertical,
            System.Drawing.Drawing2D.HatchStyle.Wave,
            System.Drawing.Drawing2D.HatchStyle.ZigZag
        };


        public FormSpielerei()
        {
            InitializeComponent();
        }

        private void FormSpielerei_Load(object sender, EventArgs e)
        {
            /*
            //den ersten Eintrag in den Kombinationsfeldern auswählen
            comboBoxHintergrundFarbe.SelectedIndex = 0;
            comboBoxLinieFarbe.SelectedIndex = 0;
            */


            //linienfarbe auf Schwarz setzen
            linienfarbe = Color.Black;
            //hintergrundfarbe auf Schwarz setzen
            hintergrundfarbe = Color.Black;

            //Array Linienstil einlesen
            foreach (System.Drawing.Drawing2D.DashStyle element in linienstil)
                listBoxLinieStil.Items.Add("");

            //Array Hintergrundmuster einlesen
            foreach (System.Drawing.Drawing2D.HatchStyle element in fuellstil)
                listBoxHintergrundStil.Items.Add("");

            //eine Referenz auf die Zeichenfläche des Panels beschaffen
            //zeichenflaeche ist als Feld der Klasse Form1 vereinbart
            zeichenflaeche = panelZeichenbereich.CreateGraphics();
        }

        private void buttonLoeschen_Click(object sender, EventArgs e)
        {
            zeichenflaeche.Clear(panelZeichenbereich.BackColor);
        }

        private void buttonBeenden_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            //eine lokale Variable für die Größe
            int groesse = 0;
            //einen schwarzen Stift erzeugen
            Pen stift = new Pen(linienfarbe);
            //einen schwarzen Pinsel erzeugen
            SolidBrush pinsel = new SolidBrush(hintergrundfarbe);


            /*
            //je nach Wert in den Kombinationsfeldern die Farben setzen
            switch (comboBoxLinieFarbe.SelectedIndex)
            {
                case 0:
                    stift.Color = Color.Black;
                    break;
                case 1:
                    stift.Color = Color.Red;
                    break;
                case 2:
                    stift.Color = Color.Blue;
                    break;
                case 3:
                    stift.Color = Color.Green;
                    break;
            }
            */


            /*
            switch (comboBoxHintergrundFarbe.SelectedIndex)
            {
                case 0:
                    pinsel.Color = Color.Black;
                    break;
                case 1:
                    pinsel.Color = Color.Red;
                    break;
                case 2:
                    pinsel.Color = Color.Blue;
                    break;
                case 3:
                    pinsel.Color = Color.Green;
                    break;
            }
            */



            //die Dicke des Stiftes setzen
            stift.Width = Convert.ToInt32(numericUpDownLinieStaerke.Value);


            //die Größe der Figur ermitteln
            switch (trackBar1.Value)
            {
                case 1:
                    groesse = 125;
                    break;
                case 2:
                    groesse = 100;
                    break;
                case 3:
                    groesse = 75;
                    break;
            }


            //Musterauswahl
            if (listBoxLinieStil.SelectedIndex >= 0)
                stift.DashStyle = linienstil[listBoxLinieStil.SelectedIndex];


            //Figur ermitteln
            //beim Kreis und beim Rechteck auch die Füllung überprüfen
            //auf Animation überprüfen
            if (radioButtonOhne.Checked == true)
            {
                if (radioButtonKreis.Checked == true)
                {
                    if (radioButtonHintergrundOhne.Checked == true)
                    {
                        zeichenflaeche.DrawEllipse(stift, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Top + groesse, panelZeichenbereich.ClientRectangle.Width - (groesse * 2), panelZeichenbereich.ClientRectangle.Height - (groesse * 2));
                    }

                    if (radioButtonHintergrundFarbe.Checked == true)
                    {
                        zeichenflaeche.FillEllipse(pinsel, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Top + groesse, panelZeichenbereich.ClientRectangle.Width - (groesse * 2), panelZeichenbereich.ClientRectangle.Height - (groesse * 2));
                    }

                    //soll mit Muster gezeichnet werden und ist ein Muster ausgewählt?
                    if (radioButtonHintergrundMuster.Checked == true && listBoxHintergrundStil.SelectedIndex >= 0)
                    {
                        //einen neuen Pinsel für das Muster erzeugen
                        //die Vordergrundfarbe kommt vom Stift, der Hintergrund ist immer weiß
                        System.Drawing.Drawing2D.HatchBrush musterPinsel = new System.Drawing.Drawing2D.HatchBrush(fuellstil[listBoxHintergrundStil.SelectedIndex], stift.Color, Color.White);

                        zeichenflaeche.FillEllipse(musterPinsel, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Top + groesse, panelZeichenbereich.ClientRectangle.Width - (groesse * 2), panelZeichenbereich.ClientRectangle.Height - (groesse * 2));
                    }
                }

                if (radioButtonRechteck.Checked == true)
                {
                    if (radioButtonHintergrundOhne.Checked == true)
                    {
                        zeichenflaeche.DrawRectangle(stift, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Top + groesse, panelZeichenbereich.ClientRectangle.Width - (groesse * 2), panelZeichenbereich.ClientRectangle.Height - (groesse * 2));
                    }

                    if (radioButtonHintergrundFarbe.Checked == true)
                    {
                        zeichenflaeche.FillRectangle(pinsel, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Top + groesse, panelZeichenbereich.ClientRectangle.Width - (groesse * 2), panelZeichenbereich.ClientRectangle.Height - (groesse * 2));
                    }
                }

                if (radioButtonLinie.Checked == true)
                {
                    zeichenflaeche.DrawLine(stift, panelZeichenbereich.ClientRectangle.Left + groesse, panelZeichenbereich.ClientRectangle.Height / 2, panelZeichenbereich.ClientRectangle.Width - groesse, panelZeichenbereich.ClientRectangle.Height / 2);
                }
            }
            else
            {
                if (radioButtonHintergrundMuster.Checked == true || radioButtonHintergrundFarbe.Checked == true)
                {
                    MessageBox.Show("Bei einer Animation ist es leider nicht möglich, eine Hintergrundfarbe oder ein Hintergrundmuster festzulegen.""Fehler Animation");
                }
                else
                {
                    int geschwindigkeit = trackBarAnimationGeschwindigkeit.Value;
                    int wiederholung = Convert.ToInt32(numericUpDownAnimationWiederholungen.Value);


                    if (radioButtonKreis.Checked == true)
                    {
                        System.Threading.Thread.Sleep(100);
                        zeichenflaeche.Clear(Color.White);                     
                    }

                    if (radioButtonRechteck.Checked == true)
                    {
                        System.Threading.Thread.Sleep(100);
                        zeichenflaeche.Clear(Color.White);
                    }

                    if (radioButtonLinie.Checked == true)
                    {
                        System.Threading.Thread.Sleep(100);
                        zeichenflaeche.Clear(Color.White);
                    }
                }
            }
        }

        private void buttonLinieFarbe_Click(object sender, EventArgs e)
        {
            //den Dialog zur Farbauswahl anzeigen
            if(colorDialogLinie.ShowDialog() == DialogResult.OK)
            {
                //die Hintergrundfarbe für das Panel auf die ausgewählte Farbe setzen
                panelLinieFarbeVorschau.BackColor = colorDialogLinie.Color;

                //und die Linienfarbe
                //linienfarbe ist ein Feld der Klasse Form1
                linienfarbe = colorDialogLinie.Color;
            }
        }

        private void buttonHintergrundFarbe_Click(object sender, EventArgs e)
        {
            //den Dialog zur Farbauswahl anzeigen
            if(colorDialogHintergrund.ShowDialog() == DialogResult.OK)
            {
                //die Hintergrundfarbe für das Panel auf die ausgewählte Farbe setzen
                panelHintergrundFarbeVorschau.BackColor = colorDialogHintergrund.Color;

                //und die eigentliche Hintergrundfarbe
                //hintergrundfarbe ist ein Feld der Klasse Form1
                hintergrundfarbe = colorDialogHintergrund.Color;

                //die Auswahl Farbe aktivieren
                radioButtonHintergrundFarbe.Checked = true;
            }
        }

        private void listBoxLinieStil_DrawItem(object sender, DrawItemEventArgs e)
        {
            //eine lokale Variable für die Berechnung der Mitte
            int y;
            //ein neuer lokaler Stift
            Pen boxStift = new Pen(Color.Black);

            //die Mitte berechnen
            y = (e.Bounds.Top + e.Bounds.Bottom) / 2;

            //den Hintergrund zeichnen
            e.DrawBackground();

            //und die Linie
            boxStift.DashStyle = linienstil[e.Index];
            e.Graphics.DrawLine(boxStift, e.Bounds.Left + 1, y, e.Bounds.Right - 1, y);
        }

        private void listBoxHintergrundStil_DrawItem(object sender, DrawItemEventArgs e)
        {
            //ein neuer lokaler Pinsel für das Muster
            System.Drawing.Drawing2D.HatchBrush boxPinsel = new System.Drawing.Drawing2D.HatchBrush(fuellstil[e.Index], Color.Black, Color.White);

            //den Hintergrund zeichnen
            e.DrawBackground();

            //und das Rechteck
            e.Graphics.FillRectangle(boxPinsel, e.Bounds.Left + 1, e.Bounds.Top + 1, e.Bounds.Width - 1, e.Bounds.Height - 1);
        }
    }
}


Th69 - Di 17.04.18 08:33

Ich sehe aber keinen Animationscode.

Und darf ich fragen, was das für ein Lernbuch ist? Denn der Code enthält einige Anti-Pattern bzgl. WinForms-Programmierung:
- Zeichnen sollte man nur im OnPaint bzw. Paint-Ereignis (auch hier wieder mein Standard-Link dazu: [Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox) [ http://www.mycsharp.de/wbb2/thread.php?threadid=21782]
Tipp: Laß mal etwas zeichnen, dann minimiere dein Fenster und zeige es wieder an...
- Der UI-Thread darf nicht blockiert werden, d.h. Thread.Sleep ist absolutes Tabu (WinForms-Programmierung ist nicht wie bei einem Konsolenprogramm)!
- Alle lokalen Grafikobjekte müssen wieder 'disposed' werden, s. meine Antwort ("PS") in Mehrere erstellte Images flackern teilweise [https://www.entwickler-ecke.de/viewtopic.php?p=711938#711938]


CodeKiddy - Fr 20.04.18 16:03

Du siehst keinen Animationscode, weil ich den einfach nicht hinbekomme. Ich weiß nicht, wie ich den aufbauen soll. Mit einer for-Schleife und innen drin if-Abfragen? Lieber eine while-Schleife? Ich stehe total auf dem Schlauch. Dort, wo die Codezeilen mit dem System.Threading.Thread.Sleep sind, soll letztendlich der Animationscode hinkommen.

Ich mache einen Fernkurs bei der ILS Hamburg zum Thema Programmieren mit C#. Die stellen die Lernhefte bereit.
Ich stehe noch ziemlich am Anfang meiner Programmierkenntnisse. Vielleicht wird das, was du ansprichst, in einem späteren Lernheft genauer unter die Lupe genommen, und sie haben darauf erst einmal verzichtet, weil es vielleicht als "zu schwierig" für den Anfang angesehen wird. Ich weiß es nicht, aber ich kann meinen Fernlehrer gerne mal auf die Probleme hinweisen, die du mir genannt hast. :)
Meine Hausaufgaben sind immer genau vorgeschrieben. Den größten Teil des Codes haben wir im Lernheft "gemeinsam" erstellt, d.h. er war vorgeschrieben und ich musste nur abschreiben. Als Beschreibung zu meiner HA stand nur, dass ich den Code um die gewünschten Funktionen erweitern soll. Weshalb ich davon ausgehe, dass die "Fehler", die sich hier noch im Code verstecken, in einem der nächsten Lernhefte behandelt werden. Hoffe ich. Wenn nicht, wäre das ziemlich...schlecht. :D


Delete - Fr 20.04.18 19:31

- Nachträglich durch die Entwickler-Ecke gelöscht -