C# WinForm实现单张图片随机切换显示位置的技术咨询
Solution: Single Image Random Position Switching in WinForms
Got it, let's adjust your code so we get a single image moving to random positions instead of spawning multiple copies all over the form. The original approach using CreateGraphics just draws new images on top each iteration—we'll use PictureBox controls instead, which are far cleaner for this kind of dynamic image positioning in WinForms.
Modified Full Code
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; namespace War_Game { public partial class Form1 : Form { private Thread _allyThread; private Thread _foeThread; private readonly Random _rdm = new Random(); private PictureBox _allyPic; private PictureBox _foePic; public Form1() { InitializeComponent(); SetupPictureBoxes(); } private void SetupPictureBoxes() { // Initialize Ally PictureBox _allyPic = new PictureBox { Size = new Size(30, 30), Image = new Bitmap(@"C:\Users\x\Downloads\sandler.png"), SizeMode = PictureBoxSizeMode.StretchImage, Visible = false }; Controls.Add(_allyPic); // Initialize Foe PictureBox _foePic = new PictureBox { Size = new Size(30, 30), Image = new Bitmap(@"C:\Users\x\Downloads\nicolas.png"), SizeMode = PictureBoxSizeMode.StretchImage, Visible = false }; Controls.Add(_foePic); } private void ally_Click(object sender, EventArgs e) { // Stop existing thread if running to avoid duplicates if (_allyThread?.IsAlive == true) _allyThread.Abort(); _allyThread = new Thread(MoveAllyImage); _allyThread.Start(); } private void MoveAllyImage() { _allyPic.Invoke((MethodInvoker)(() => _allyPic.Visible = true)); for (int i = 0; i < 100; i++) { // Calculate random position that keeps the image inside the form int x = _rdm.Next(0, ClientSize.Width - _allyPic.Width); int y = _rdm.Next(0, ClientSize.Height - _allyPic.Height); // Update PictureBox location safely via Invoke (UI thread only!) _allyPic.Invoke((MethodInvoker)(() => { _allyPic.Location = new Point(x, y); })); Thread.Sleep(100); } _allyPic.Invoke((MethodInvoker)(() => _allyPic.Visible = false)); MessageBox.Show("Adam Sandler has invaded your program!"); } private void foe_Click(object sender, EventArgs e) { // Stop existing thread if running if (_foeThread?.IsAlive == true) _foeThread.Abort(); _foeThread = new Thread(MoveFoeImage); _foeThread.Start(); } private void MoveFoeImage() { _foePic.Invoke((MethodInvoker)(() => _foePic.Visible = true)); for (int i = 0; i < 100; i++) { int x = _rdm.Next(0, ClientSize.Width - _foePic.Width); int y = _rdm.Next(0, ClientSize.Height - _foePic.Height); _foePic.Invoke((MethodInvoker)(() => { _foePic.Location = new Point(x, y); })); Thread.Sleep(100); } _foePic.Invoke((MethodInvoker)(() => _foePic.Visible = false)); MessageBox.Show("Nicolas Cage has invaded your program!"); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // Clean up threads on form close to avoid memory leaks _allyThread?.Abort(); _foeThread?.Abort(); } } }
Key Changes Explained
- PictureBox Controls: We use
PictureBoxinstead of directCreateGraphicsdrawing—this lets us easily update the image's position without leaving old images on the form. - Safe Cross-Thread Operations: WinForms controls can only be modified from the UI thread, so we use
Control.Invoketo update thePictureBoxlocation and visibility from our background threads. - Thread Safety: We check if a thread is already running before starting a new one, and clean up threads when the form closes to prevent memory leaks.
- Boundary Checks: When calculating random positions, we subtract the image's width/height from the form's client size to ensure the entire image stays visible inside the form.
- Clean Visibility: We hide the
PictureBoxwhen the movement loop finishes, keeping the form tidy after the invasion message.
内容的提问来源于stack exchange,提问作者user11526364
相关产品推荐
相关产品推荐

