PictureBox остава изчертан в старата позиция при преместване на по-нова позиция

Уча C# с помощта на книгата Head First C#. В първата лаборатория, когато създавах играта Greyhound Racing, се натъкнах на известно поведение и не разбирам защо кодът ми се изобразява така, както е. При първото щракване на бутона Race конете се състезават до края на пистата, но те се изобразяват така, че всеки създава следа от предишни изображения зад себе си, докато стигнат до края на пистата, след което предходните изображения най-накрая изчезват. При следващи щраквания върху бутона Race се случва същото, но също така не успява да изтрие PictureBox за всяко куче от финалната линия до завършване на текущото състезание. въведете описание на изображението тук

Ето кратък видеоклип от 19 секунди, който демонстрира какво имам предвид: Пример за изображения в края

Защо кучетата „проследяват“ по време на състезанието и защо не изчезват от финалната линия, след като бъдат изобразени отново на старта до завършването на следващото състезание? Мисля, че когато кучетата се преместят в TakeStartingPosition(), те ще бъдат преместени, а не преначертани. Същото с Run(), бих си помислил, че всяка нова позиция е ход, а не преначертаване, но изглежда, че преначертава изображението при всяка стъпка на движение и не изтрива старото до самия край на състезанието. Какво правя неправилно?

Greyhound.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace RaceTrackSimulator
{
    class Greyhound
    {
        public int StartingPosition;
        public int RacetrackLength;
        public PictureBox MyPictureBox = null;
        public int Location = 0;
        public Random Randomizer;

        public bool Run()
        {
            // Move forward either 1, 2, 3 or 4 spaces at random
            int moveSpaces = Randomizer.Next(1, 4);


            // Update the position of my Picturebox on the form like this:
            //  MyPictureBox.Left = StartingPosition + Location;
            MyPictureBox.Left = StartingPosition + Location;

            // Return true if I won the race
            if (Location >= RacetrackLength)
            {
                return true;
            }
            else
            {
                Location += moveSpaces;
                return false;
            }
        }

        public void TakeStartingPosition()
        {
            // Reset my location to 0 and my PictureBox to starting position
            Location = 0;
            MyPictureBox.Left = StartingPosition;

        }
    }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RaceTrackSimulator
{
    public partial class Form1 : Form
    {
        Greyhound[] Dogs;

        public Form1()
        {
            InitializeComponent();
            Random MyRandomizer = new Random();

            // Initialize Dogs
            Dogs = new Greyhound[4];

            Dogs[0] = new Greyhound()
            {
                MyPictureBox = pictureBox2,
                StartingPosition = racetrackPictureBox.Left,
                RacetrackLength = racetrackPictureBox.Width - pictureBox2.Width,
                Randomizer = MyRandomizer
            };

            Dogs[1] = new Greyhound()
            {
                MyPictureBox = pictureBox3,
                StartingPosition = racetrackPictureBox.Left,
                RacetrackLength = racetrackPictureBox.Width - pictureBox3.Width,
                Randomizer = MyRandomizer
            };

            Dogs[2] = new Greyhound()
            {
                MyPictureBox = pictureBox4,
                StartingPosition = racetrackPictureBox.Left,
                RacetrackLength = racetrackPictureBox.Width - pictureBox4.Width,
                Randomizer = MyRandomizer
            };

            Dogs[3] = new Greyhound()
            {
                MyPictureBox = pictureBox5,
                StartingPosition = racetrackPictureBox.Left,
                RacetrackLength = racetrackPictureBox.Width - pictureBox5.Width,
                Randomizer = MyRandomizer
            };
        }

        private void raceButton_Click(object sender, EventArgs e)
        {
            bool winner = false;
            int winningDog = 0;

            for (int eachDog = 0; eachDog < Dogs.Length; eachDog++)
            {
                Dogs[eachDog].TakeStartingPosition();
            }

            while (!winner)
            {
                for (int i = 0; i < 4; i++)
                {
                    if (Dogs[i].Run())
                    {
                        winner = true;
                        winningDog = i+1;
                    }
                    System.Threading.Thread.Sleep(1);
                }
            }

            MessageBox.Show("Winning Dog is #" + winningDog);
        }
    }
}

Form1.Designer.cs

namespace RaceTrackSimulator
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.pictureBox2 = new System.Windows.Forms.PictureBox();
            this.racetrackPictureBox = new System.Windows.Forms.PictureBox();
            this.pictureBox3 = new System.Windows.Forms.PictureBox();
            this.pictureBox4 = new System.Windows.Forms.PictureBox();
            this.pictureBox5 = new System.Windows.Forms.PictureBox();
            this.raceButton = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.racetrackPictureBox)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).BeginInit();
            this.SuspendLayout();
            // 
            // pictureBox2
            // 
            this.pictureBox2.Image = global::RaceTrackSimulator.Properties.Resources.dog;
            this.pictureBox2.Location = new System.Drawing.Point(13, 22);
            this.pictureBox2.Name = "pictureBox2";
            this.pictureBox2.Size = new System.Drawing.Size(75, 20);
            this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pictureBox2.TabIndex = 2;
            this.pictureBox2.TabStop = false;
            // 
            // racetrackPictureBox
            // 
            this.racetrackPictureBox.Image = global::RaceTrackSimulator.Properties.Resources.racetrack;
            this.racetrackPictureBox.Location = new System.Drawing.Point(13, 12);
            this.racetrackPictureBox.Name = "racetrackPictureBox";
            this.racetrackPictureBox.Size = new System.Drawing.Size(600, 200);
            this.racetrackPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.racetrackPictureBox.TabIndex = 0;
            this.racetrackPictureBox.TabStop = false;
            // 
            // pictureBox3
            // 
            this.pictureBox3.Image = global::RaceTrackSimulator.Properties.Resources.dog;
            this.pictureBox3.Location = new System.Drawing.Point(13, 74);
            this.pictureBox3.Name = "pictureBox3";
            this.pictureBox3.Size = new System.Drawing.Size(75, 20);
            this.pictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pictureBox3.TabIndex = 3;
            this.pictureBox3.TabStop = false;
            // 
            // pictureBox4
            // 
            this.pictureBox4.Image = global::RaceTrackSimulator.Properties.Resources.dog;
            this.pictureBox4.Location = new System.Drawing.Point(13, 126);
            this.pictureBox4.Name = "pictureBox4";
            this.pictureBox4.Size = new System.Drawing.Size(75, 20);
            this.pictureBox4.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pictureBox4.TabIndex = 4;
            this.pictureBox4.TabStop = false;
            // 
            // pictureBox5
            // 
            this.pictureBox5.Image = global::RaceTrackSimulator.Properties.Resources.dog;
            this.pictureBox5.Location = new System.Drawing.Point(13, 178);
            this.pictureBox5.Name = "pictureBox5";
            this.pictureBox5.Size = new System.Drawing.Size(75, 20);
            this.pictureBox5.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pictureBox5.TabIndex = 5;
            this.pictureBox5.TabStop = false;
            // 
            // raceButton
            // 
            this.raceButton.Location = new System.Drawing.Point(538, 377);
            this.raceButton.Name = "raceButton";
            this.raceButton.Size = new System.Drawing.Size(75, 23);
            this.raceButton.TabIndex = 6;
            this.raceButton.Text = "RACE!";
            this.raceButton.UseVisualStyleBackColor = true;
            this.raceButton.Click += new System.EventHandler(this.raceButton_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(625, 412);
            this.Controls.Add(this.raceButton);
            this.Controls.Add(this.pictureBox5);
            this.Controls.Add(this.pictureBox4);
            this.Controls.Add(this.pictureBox3);
            this.Controls.Add(this.pictureBox2);
            this.Controls.Add(this.racetrackPictureBox);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.racetrackPictureBox)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.PictureBox racetrackPictureBox;
        private System.Windows.Forms.PictureBox pictureBox2;
        private System.Windows.Forms.PictureBox pictureBox3;
        private System.Windows.Forms.PictureBox pictureBox4;
        private System.Windows.Forms.PictureBox pictureBox5;
        private System.Windows.Forms.Button raceButton;
    }
}

person Vin Breau    schedule 22.10.2013    source източник


Отговори (1)


Изпълнявате тесен цикъл в манипулатора на манипулатора за щракване върху бутон, който монополизира основната нишка на потребителския интерфейс. Когато кучето се движи напред, формата трябва да се пребоядиса, за да „изтрие“ мястото, където кучето е било преди това. Тъй като обаче кодът е блокиран в цикъла, той не може да се преначертае. По същия начин, когато състезанието се рестартира, кучетата не изчезват от финалната линия по същата причина.

Едно възможно "бързо решение" е да извикате Application.DoEvents(); в кода, за да позволите на формуляра да се актуализира. Това би изглеждало така:

    private void raceButton_Click(object sender, EventArgs e)
    {
        bool winner = false;
        int winningDog = 0;

        for (int eachDog = 0; eachDog < Dogs.Length; eachDog++)
        {
            Dogs[eachDog].TakeStartingPosition();
        }
        Application.DoEvents();

        while (!winner)
        {
            for (int i = 0; i < 4; i++)
            {
                if (Dogs[i].Run())
                {
                    winner = true;
                    winningDog = i+1;
                }
                Application.DoEvents();
                System.Threading.Thread.Sleep(1);
            }
        }

        MessageBox.Show("Winning Dog is #" + winningDog);
    }

Това обаче е просто лейкопласт върху истинския проблем: Не трябва да монополизирате основната нишка на потребителския интерфейс с дълъг цикъл в манипулатора за щракване на бутон.

Едно възможно решение е да нулирате кучетата в манипулатора за щракване на бутон, след което да стартирате Таймер. В събитието Tick() на Timer() ще извикате метода Run() на всяко куче и ще проверите за победител. Когато състезанието бъде спечелено, изключете отново таймера.

person Idle_Mind    schedule 22.10.2013
comment
Благодаря. Това определено беше липсващото знание, от което се нуждаех. Мога да стартирам таймер, който да съдържа състезанието, и да деактивирам бутона за състезание, докато таймерът тече през състезанието. - person Vin Breau; 23.10.2013
comment
перфектен Извикването на Sleep() също не трябва да е необходимо в цикъла. Можете да регулирате скоростта на кучетата, като промените Interval() на таймера. DoEvents() също вече не е необходим, след като сте превключили към Timer, тъй като нормалните съобщения (като рисуване) ще бъдат обработени между събитията Tick(). - person Idle_Mind; 23.10.2013