0

我正在使用 Head First C# 书学习 C#。在构建 Greyhound Racing 游戏的第一个实验室中,我遇到了一些行为,但我不明白为什么我的代码会以这种方式呈现。在第一次点击 Race 按钮时,马匹会跑到赛道的尽头,但它们会被渲染成它们各自在它们身后创建一条先前图像的轨迹,直到它们到达赛道的尽头,而先前的图像最终消失了。在随后单击 Race 按钮时,会发生同样的事情,但它也无法从终点线擦除每只狗的 PictureBox,直到当前比赛完成。 在此处输入图像描述

这是一个简短的 19 秒视频,演示了我的意思:尾随图像示例

为什么狗在比赛中会“尾随”,为什么在下一场比赛完成之前,它们在重新开始时没有从终点线消失?我认为当狗被重新定位时,TakeStartingPosition()它们会被移动,而不是重新绘制。与 相同Run(),我认为每个新位置都是一个动作,而不是重绘,但它似乎在每个动作步骤都重绘图像,直到比赛结束才抹掉旧位置。我做错了什么?

灰狗.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;
    }
}
4

1 回答 1

1

您正在按钮单击处理程序处理程序中运行一个紧密循环,该处理程序垄断了主 UI 线程。当狗向前移动时,由表格重新绘制自己以“擦除”狗之前所在的位置。然而,由于代码卡在循环中,它不能重绘自己。同样,当比赛重新开始时,狗不会出于同样的原因从终点线消失。

一种可能的“快速修复”是调用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);
    }

然而,这只是真正问题之上的创可贴: 您不应该在按钮单击处理程序中使用长时间运行的循环来垄断主 UI 线程。

一种可能的解决方案是在按钮单击处理程序中重置狗,然后启动Timer。在 Timer() 的 Tick() 事件中,您将调用每只狗的 Run() 方法并检查获胜者。赢得比赛后,关闭计时器。

于 2013-10-22T21:02:34.343 回答