Search code examples
c#wpfwhile-loopuicontrolstarvation

implement while loop without starvation for redrawing an image in wpf


i have a wpf form and a canvas in it and an image in canvas. i want this image to move randomly to directions every n milliseconds. i used while loop but it stocked in starvation. so i had no way to implement this movement by one mouse event to see how good is my idea about this movement. this is my code below:

public void movebox()
        {
            Random rnd = new Random();
            int movex = rnd.Next(0, 2);
            int movey = rnd.Next(0, 2);
            Canvas.SetTop(image1, Canvas.GetTop(image1) + (movey == 1 ? 1 : -1));
            Canvas.SetLeft(image1, Canvas.GetLeft(image1) + (movex == 1 ? 1 : -1));
        }
private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            movebox();
        }

i likes movement of image but now i have same problem yet. i need this object to move automatically forever every n milliseconds without starvation. this is my ideal code below:

while(true){
                Random rnd = new Random();
                int movex = rnd.Next(0, 2);
                int movey = rnd.Next(0, 2);
                Canvas.SetTop(image1, Canvas.GetTop(image1) + (movey == 1 ? 1 : -1));
                Canvas.SetLeft(image1, Canvas.GetLeft(image1) + (movex == 1 ? 1 : -1));
                Thread.Sleep(1000);
            }

what should i have do now?


Solution

  • One thing you can do if you really want a loop is to start another thread and use that. This way your UI will stay responsive.

    public void movebox()
    {
        Task.Run(() => 
        {
            while(true)
            {
                Random rnd = new Random();
                int movex = rnd.Next(0, 2);
                int movey = rnd.Next(0, 2);
                Dispatcher.Invoke(() => {
                    Canvas.SetTop(image1, Canvas.GetTop(image1) + (movey == 1 ? 1 : -1));
                    Canvas.SetLeft(image1, Canvas.GetLeft(image1) + (movex == 1 ? 1 : -1));
                });
                Thread.Sleep(1000);
            }
        });
    }
    

    A better approach however is to use Timer like this:

    System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    dispatcherTimer.Tick += dispatcherTimer_Tick;
    dispatcherTimer.Interval = new TimeSpan(0,0,1);
    dispatcherTimer.Start();
    
    
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        Random rnd = new Random();
        int movex = rnd.Next(0, 2);
        int movey = rnd.Next(0, 2);
        Canvas.SetTop(image1, Canvas.GetTop(image1) + (movey == 1 ? 1 : -1));
        Canvas.SetLeft(image1, Canvas.GetLeft(image1) + (movex == 1 ? 1 : -1));
    }
    

    Disclaimer: I have written the code in browser.