2015-10-07 9 views
13

Ho una classe Cane con un metodo Run che si suppone di spostare le immagini sullo schermo:Metodi esecuzione contemporaneamente

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; 
} 

Questo metodo viene chiamato da un evento click del pulsante in cui vengono creati 4 oggetti cane e il ogni oggetto chiama il metodo 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(); 
} 

Il problema è che ciascun metodo viene eseguito uno alla volta, non contemporaneamente. Voglio che ogni metodo sia eseguito allo stesso tempo. Se rimuovo l'istruzione while, allora tutti i metodi vengono eseguiti allo stesso tempo, ma con il ciclo while, vengono eseguiti uno dopo l'altro. Qualsiasi suggerimento su come risolvere questo problema è molto apprezzato. metodo eseguito senza ciclo 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

Aggiungere un timer e chiamare i metodi da lì. Quindi non dovrai fare confusione con i thread, che causano problemi con gli elementi dell'interfaccia utente se non sai cosa stai facendo. –

risposta

4

Prova questa

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

e per il While Loop pure usate questo

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

BeginInvoke è asincrona; pertanto, il controllo ritorna immediatamente all'oggetto chiamante dopo che è stato chiamato. BeginInvoke restituisce un oggetto DispatcherOperation che può essere utilizzato per interagire con il delegato quando il delegato si trova nella coda eventi. L'oggetto DispatcherOperation restituito da BeginInvoke può essere utilizzato in vari modi per interagire con il delegato specificato

+2

Questo non risolve il problema del ciclo while. –

+0

Dice che ho bisogno di un riferimento a un oggetto per usare Dispatcher. Cosa dovrei fare? Scusa, non ho molta esperienza con questa roba. – grammer

+1

La domanda riguardava PictureBox, che sono WinForms. Stai parlando di Dispatcher, che è WPF - dubito che possa giocare bene con il ciclo degli eventi di WinForms. –

13

Animazione e WinForms in genere non è semplice. Quello che i programmatori di solito fanno è impostare un ciclo di gioco. Un ciclo di gioco fa tre cose: recupera l'input dell'utente, aggiorna la nuova posizione degli sprite e poi disegna gli sprite sullo schermo.

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(); 
     }); 
    } 

} 

Quindi la classe del cane si presenta così. Ha bisogno di Update sua posizione ogni volta che il timer zecche, e quindi disegnare la sua posizione:

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

Come questo approccio, ma dovrei riutilizzare l'istanza 'Random' e inviare le chiamate di disegno usando' BeginInvoke' nella casella di forma/immagine. – Gene

+0

Buon ritiro! Grazie! –

+0

Grazie per il vostro aiuto, ma questo sembra troppo avanzato per me. Non riesco davvero a dare un senso. Non ho molta esperienza con C#. – grammer

0

Perché non provare questo?

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

Per utilizzare correttamente questa istruzione è necessario creare un elenco di Cane.

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())); 

Se non ti piace elenco:

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

di interagire con l'interfaccia utente da un thread diverso, è necessario utilizzare un delegato e chiamando Control.Invoke/BeginInvoke. È possibile verificare se è necessario chiamare Invoke utilizzando la proprietà InvokeRequired. Quindi nel tuo caso:

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

questo ha provocato errori. – grammer

+0

Ho modificato la risposta – Donald

+0

Ho provato. Ne risultava un altro messaggio di errore: Informazioni aggiuntive: Operazione cross-thread non valida: Controllo 'pictureDog4' a cui si accede da un thread diverso dal thread creato su – grammer

Problemi correlati