3

I have a list of Image tags in my XAML that I wish to update one after the other and sleep inbetween.

I have the following code, but they all just update at once and the UI remains frozen until it is finished.

Please can you help me to make it update 'live' as the image sources are set?

Here's what I have so far:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace JobMonitor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Add the test lights to a list
        private readonly Dictionary<int, Image> imageDictionary;

        public MainWindow()
        {
            InitializeComponent();

            imageDictionary = new Dictionary<int, Image>
            {
                {1, TestLight1},
                {2, TestLight2},
                {3, TestLight3},
                {4, TestLight4},
                {5, TestLight5},
                {6, TestLight6},
            };
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Dispatcher.InvokeAsync(ChangeImage);

        }

        private void ChangeImage()
        {
            // Loop through each of the tests
            foreach (var testLight in imageDictionary)
            {
                ChangeImageLights(testLight.Value);

                Thread.Sleep(1000);
            }
        }

        private void ChangeImageLights(Image img)
        {

            var myImage3 = new Image();
            var redLightImage = new BitmapImage();
            redLightImage.BeginInit();
            redLightImage.UriSource = new Uri("red_light.png", UriKind.Relative);
            redLightImage.EndInit();
            myImage3.Stretch = Stretch.Fill;
            myImage3.Source = redLightImage;

            img.Source = redLightImage;
        }
    }
}

XAML:

<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight1" />
<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight2" />
<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight3" />
<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight4" />
<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight5" />
<Image Margin="0,0,20,0" Height="40" Source="green_light.png" Stretch="Fill" Name="TestLight6" />

I thought that using the Dispatcher.InvokeAsync() would be the answers to my troubles which is why I have put it in there....

2 Answers 2

3

You can deduce that InvokeAsync isn't actually running on another thread because you're allowed to perform the operations in ChangeImageLights. One approach would be to leverage a BackgroundWorker:

// new private class variable
private BackgroundWorker _worker = new BackgroundWorker();

// constructor code
public .ctor()
{
    _worker.WorkerReportsProgress = true;
    _worker.DoWork += (s, e) =>
    {
        // Loop through each of the tests
        foreach (var testLight in imageDictionary)
        {
            _worker.ReportProgress(1, testLight.Value);

            Thread.Sleep(1000);
        }
    }
    _worker.ProgressChanged += (s, e) =>
    {
        var myImage3 = new Image();
        var redLightImage = new BitmapImage();
        redLightImage.BeginInit();
        redLightImage.UriSource = new Uri("red_light.png", UriKind.Relative);
        redLightImage.EndInit();
        myImage3.Stretch = Stretch.Fill;
        myImage3.Source = redLightImage;

        ((Image)e.UserState).Source = redLightImage;
    }
}

The BackgroundWorker starts up a new thread and runs the DoWork handler on that thread. Then when you call ReportProgress it handles switching threads so that you can actually modify the UI in the ProgressChanged handler.

Now the background thread will actually sleep for 1 second before it reports more progress.

Sign up to request clarification or add additional context in comments.

Comments

2

How about using async/await ?

async private void ChangeImage()
{
    // Loop through each of the tests
    foreach (var testLight in imageDictionary)
    {
        ChangeImageLights(testLight.Value);

        await Task.Delay(1000);
    }
}

That way you wouldn't need BackgroundWorker. Just invoke ChangeImage

private void Button_Click(object sender, RoutedEventArgs e)
{
    ChangeImage();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.