2015-10-07 63 views
13

我有這應該在屏幕上移動圖像的方法運行狗類:運行方法同時

public bool Run() 
{ 
    Point p = PictureBoxDog.Location; 

    while(p.X < 530) 
    { 
     int movement = Randomizer.Next(0, 3); 
     p.X += movement; 
     PictureBoxDog.Location = p; 
    } 

    if (Location == 4) //Incomplete section. 
     return true; 
    else 
     return false; 
} 

這種方法是,其中4個狗對象創建一個按鈕單擊事件和被叫每個對象調用Run方法:

private void button1_Click(object sender, EventArgs e) 
{ 
    Dog dog1 = new Dog(pictureDog1); 
    Dog dog2 = new Dog(pictureDog2); 
    Dog dog3 = new Dog(pictureDog3); 
    Dog dog4 = new Dog(pictureDog4); 

    dog1.Run(); 
    dog2.Run(); 
    dog3.Run(); 
    dog4.Run(); 
} 

的問題是,每一種方法執行一個接一個,不能同時。我希望每個方法同時運行。如果我刪除了while語句,那麼所有的方法都會同時執行,但是在while循環中,它們會一個接一個地執行。有關如何解決這個問題的任何建議,非常感謝。而不while循環運行方法:

public bool Run() //Dog1.Run() 
{ 
    Point p = PictureBoxDog.Location; 

    int movement = Randomizer.Next(0, 30); 
    //Location += movement; 

    p.X += movement; 
    PictureBoxDog.Location = p; 

    if (Location == 4) //Incomplete code. 
    return true; 
    else 
    return false; 
} 
+2

添加一個計時器並從那裏調用方法。那麼,如果你不知道自己在做什麼,那麼你就不必亂用線程,這會導致UI元素出現問題。 –

回答

4

嘗試此

Dispatcher.BeginInvoke((Action) (() => 
{ 
    dog1.Run(); 
})); 
Dispatcher.BeginInvoke((Action) (() => 
{ 
    dog2.Run(); 
})); 
Dispatcher.BeginInvoke((Action) (() => 
{ 
    dog3.Run(); 
})); 
Dispatcher.BeginInvoke((Action) (() => 
{ 
    dog4.Run(); 
})); 

和While循環以及使用此

Dispatcher.BeginInvoke((Action) (() => 
{ 
    while(p.X < 530) 
    { 
     int movement = Randomizer.Next(0, 3); 
     p.X += movement; 
     PictureBoxDog.Location = p; 
    } 
})); 

BeginInvoke的是異步的;因此,控件在被調用後立即返回給調用對象。 BeginInvoke返回一個DispatcherOperation對象,該對象可用於在委託位於事件隊列中時與委託進行交互。由BeginInvoke返回的DispatcherOperation對象可以以多種方式用於與指定的代理交互

+2

這並不解決while循環問題。 –

+0

它說我需要一個對象引用來使用分派器。我應該做些什麼?對不起,我對這個東西不是很有經驗。 – grammer

+1

問題涉及PictureBoxes,這是WinForms。你在談論Dispatcher,這是WPF - 我懷疑它會在WinForms事件循環中發揮作用。 –

13

動畫和WinForms通常不是直截了當的。程序員通常做的是建立一個遊戲循環。遊戲循環執行三件事 - 獲取用戶輸入,更新精靈的新位置,然後在屏幕上繪製精靈。

using System.Threading; 

public partial class Form1 
{ 
    private Timer _timer; 
    private Dog _dog1, _dog2, _dog3, _dog4; 

    public void InitializeComponent() 
    { 
     SetupDogs(); 

     // Every quarter of a second, run the function GameLoop 
     _timer = new Timer(GameLoop, null, 
     TimeSpan.FromSeconds(0.25), 
     TimeSpan.FromSeconds(0.25)); 
    } 

    private void SetupDogs() 
    { 
     _dog1 = new Dog(PictureBoxDog1); 
     _dog2 = new Dog(PictureBoxDog2); 
     _dog3 = new Dog(PictureBoxDog3); 
     _dog4 = new Dog(PictureBoxDog4); 

    } 

    public void GameLoop(object state) 
    { 
     GetUserInput(); 
     Update(); 
     Draw(); 
    } 

    public void GetUserInput() 
    { 
    // You don't need this now. But if you need to 
    // process user input later, you can do it here. 
    // 
    // e.g. if Last key 
    // pressed was arrow-left or 
    // arrow-right etc. 
    } 

    public void Update() 
    { 
    _dog1.Update(); 
    _dog2.Update(); 
    _dog3.Update(); 
    _dog4.Update(); 
    } 

    public void Draw() 
    { 
     // Draw on the main UI thread 
     Dispatcher.BeginInvoke(() => 
     { 
     _dog1.Draw(); 
     _dog2.Draw(); 
     _dog3.Draw(); 
     _dog4.Draw(); 
     }); 
    } 

} 

然後你的狗類看起來像這樣。它需要Update它的位置每次計時器滴答聲,然後得出自己的立場:

public class Dog 
{ 

    bool _isRunning = true; 

    Point Location { get; set; } 

    Point NextLocation { get; set; } 

    PictureBox PictureBoxDog { get; set; } 

    public Dog(PictureBox pictureBox) 
    { 
    PictureBoxDog = pictureBox; 

    Location = GetRandomLocation(); 

    NextLocation = GetRandomLocation(); 
    } 

    private Point GetRandomLocation() 
    { 
    var random = new Random(); 
    return new Point(random.Next(800), random.Next(800)); 
    } 

    public void Update() 
    { 
    // Calculates the new coordinates for a dog 

    // The dog starts from a random position, and is 
    // given a new random position to run towards. 

    // If the dog has arrived at the new random position, then 
    // give the dog a new random position to run to again 
    if (NextLocation.X == Location.X && NextLocation.Y == Location.Y) 
    { 
     NextLocation = GetRandomLocation(); 
    } 

    if (_isRunning) 
    { 
     // Move the dog closer to its destination 
     // dx and dy can be -1, 0, or 1 
     var dx = Math.Sign(NextLocation.X - Location.X); 
     var dy = Math.Sign(NextLocation.Y - Location.Y); 

     Location = new Point(Location.X + dx, Location.Y + dy); 
    } 
    } 

    public void Draw() 
    { 
    PictureBoxDog.Location = Location; 
    } 
} 
+0

像這種方法一樣,但我會重新使用'Random'實例並使用表單/圖片框上的'BeginInvoke'分配繪圖調用。 – Gene

+0

好皮卡!謝謝! –

+0

感謝您的幫助,但這對我來說似乎太過先進。我無法真正理解它。我對C#不是很有經驗。 – grammer

0

爲什麼不試試呢?

Task.Factory.StartNew(() => Parallel.ForEach<Dog>(Dogs, dog=> dog.run())); 

要正確使用此指令,您應該創建一個Dog列表。

List<Dog> Dogs = new List<Dog>(); 
     Dogs.Add(dog1); 
     Dogs.Add(dog2); 
     Dogs.Add(dog3); 
     Dogs.Add(dog4); 
     Task.Factory.StartNew(() => Parallel.ForEach<Dog>(Dogs, dog => dog.Run())); 

如果你不喜歡的列表:

Task.Factory.StartNew(() => dog1.Run()); 
Task.Factory.StartNew(() => dog2.Run()); 
Task.Factory.StartNew(() => dog3.Run()); 
Task.Factory.StartNew(() => dog4.Run()); 

從不同的線程與UI交互,你需要使用一個委託,並調用Control.Invoke/BeginInvoke的。 您可以測試是否需要使用InvokeRequired屬性調用Invoke。 所以在你的情況下:

if (PictureBoxDog.InvokeRequired){ 
PictureBoxDog.Invoke((MethodInvoker)(() => PictureBoxDog.location = p));} 
+0

這導致了錯誤。 – grammer

+0

我編輯了答案 – Donald

+0

試過了。它導致另一個錯誤消息:附加信息:跨線程操作無效:控制'pictureDog4'從其他線程創建的控件上執行跨線程操作時,從 – grammer