0

I have a problem where I have some values that needs to be updated every 5 sec. But in between I have to update my newsfeed too.

How do I run my newsfeed?: My newsfeed will be run while in a loop, moving the text left.

Problem: I will need somehow to run all my other code at the same time as the newsfeed is also running. But I can't figure out a way to do this. I can't place my newsfeed updater inside my main loop, because then it update with the rest too, resulting in the newsfeed bugging.

Code:

public partial class Form1 : Form
{


    ServiceReference1.monitorSoapClient ds = new ServiceReference1.monitorSoapClient();
    public Form1()
    {
        InitializeComponent();
    }

    private void lblTempOut_Click(object sender, EventArgs e)
    {

    }

    private async void btnUpdate_Click(object sender, EventArgs e)
    {
        int i = 0;

        do
        {

            lblTempOut.Text = ds.OutdoorTemp().ToString("N2") + " °C";
            lblInsideTemp.Text = ds.StockTemp().ToString("N2") + " °C";

            lblHumitidyOutside.Text = ds.OutdoorHumidity().ToString("N2") + " %";
            lblStockHumitidy.Text = ds.StockHumidity().ToString("N2") + " %";

            {
                listBoxMinItem.Items.Clear();
                ds.StockItemsUnderMin().ForEach(item =>
                {
                    listBoxMinItem.Items.Add(item);
                });
            }
            {
                listBoxMostSold.Items.Clear();
                ds.StockItemsMostSold().ForEach(item =>
                {
                    listBoxMostSold.Items.Add(item);
                });
            }
            {
               ListBoxOverMax.Items.Clear();
                ds.StockItemsUnderMin().ForEach(item =>
                {
                    ListBoxOverMax.Items.Add(item);
                });
            }

            TimeZoneInfo timeZoneCopenhagen = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
            DateTime CEST = DateTime.UtcNow;

            DateTime ConvertedTimeCEST = TimeZoneInfo.ConvertTimeFromUtc(CEST, timeZoneCopenhagen);

            lblTimeCopenhagen.Text = ConvertedTimeCEST.ToString();

            TimeZoneInfo timeZoneLondon = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
            DateTime GMT = DateTime.UtcNow;

            DateTime ConvertedTimeGMT = TimeZoneInfo.ConvertTimeFromUtc(GMT, timeZoneLondon);

            lblTimeLondon.Text = ConvertedTimeGMT.ToString();

            TimeZoneInfo timeZoneSST = TimeZoneInfo.FindSystemTimeZoneById("Singapore Standard Time");
            DateTime SST = DateTime.UtcNow;

            DateTime ConvertedTimeSST = TimeZoneInfo.ConvertTimeFromUtc(SST, timeZoneSST);
            lblTimeSingapore.Text = ConvertedTimeSST.ToString();

            string newsFeedUrl = @"https://nordjyske.dk/rss/nyheder";

            XmlReader reader = XmlReader.Create(newsFeedUrl);
            SyndicationFeed syndFeed = SyndicationFeed.Load(reader);

            reader.Close();

            foreach (SyndicationItem item in syndFeed.Items)
            {
                newsFeed.Text += item.Title.Text + "                   ";
            }
            
            await Task.Delay(5000);
            
        } while (i == 0);

        Move();
    }
    private void ResetNewsPosition()
    {
        newsFeed.Left = newsContainer.Width;
    }
    private async void Move()
    {

        while (true)
        {
            ResetNewsPosition();

            while (newsFeed.Location.X + newsFeed.Width >= 0)
            {
                newsFeed.Left -= 1;

                await Task.Delay(10);
            }
        }
    }

If anyone have any suggestions, please do let me know :)

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
SoloFeed
  • 19
  • 6
  • 3
    Is this a windows forms app? Look into background worker, or multiple separate asynchronous tasks – ADyson Jan 05 '22 at 11:58
  • Yes, sorry, it is made with .net framwork in forms :) – SoloFeed Jan 05 '22 at 11:59
  • Is this helpful? [Run async method regularly with specified interval](https://stackoverflow.com/questions/30462079/run-async-method-regularly-with-specified-interval) – Theodor Zoulias Jan 05 '22 at 12:07
  • I'm sure the CPU in your machine will be fast enough to update new news every 5 seconds while also sliding the ticker along the screen all with just one thread in your code, but it looks to me like you have some issues with the current code, as it looks possible to trap a UI thread inside a loop for a long time and I'm not sure when it's allowed to escape to do the Move. Instead consider having a timer that ticks every 10 ms, and every 500 ticks it gets the new news as an async operation. C# will fetch the news however it likes but the ui thread will be free to carry on executing the move. – Caius Jard Jan 05 '22 at 12:07
  • By the way, 10ms is quite short; does it need to be done so often? 100 times a second is quite a bit faster than the eye can appreciate – Caius Jard Jan 05 '22 at 12:09
  • @CaiusJard Sounds like a good way to solve it, the timer idea. But how do I implement that in this code? What method is used to count every tick? Sorry for the question, just new working with time and timers haha :) – SoloFeed Jan 05 '22 at 12:12
  • `int counter = 0;` in the class level, and then perhaps a Timer_Tick that looks like `if(counter >= 500){ counter = 0; var newNews = await GetNewNewsAsync(); /* do other stuff here with newNews, perhaps*/` } - when the UI thread hits the await it'll offload the getting of the news, and go back to what it normally does (processing Timer Tick), when new news comes in, it'll be called back to continue on where it left off, so it can do some *quick* processing of it if it needs to; don't tie it up for long. Remember that its the only thread, so if you have more IO to do, await again) – Caius Jard Jan 05 '22 at 12:13

2 Answers2

2

You do this by using Timers. When writing UI code you should not write loops that take longer than a few hundred ms to complete. And if your loop contains a Task.Delay or a Thread.Sleep it is a strong indicator that you should use a timer instead. Task.Delay will internally use a timer, and in some cases it can be convenient to use if you want a single short delay, but whenever you make statements like "I want to run X ever Y seconds" it will probably be better to use a timer directly.

This lets you interleave different functions, i.e. you run each function after each other on a single thread, possibly with different frequencies. For example, rewrite your move function to

private void MoveNewsPos(){
    newsFeed.Left -= 1;
    if(newsFeed.Location.X + newsFeed.Width < 0)
         ResetNewsPosition();
}

and call this from a timer with a 10ms interval.

The other option is to use multi threading. But this is mostly useful when you are doing things that are computationally expensive. It is not useful at all when most of the task consists of updating the UI.

For completeness there is also asynchronous operations that are mostly useful when doing IO work, to allow the UI thread to do other things while waiting from results from the disk or network.

see also why are there 5 timer classes for different types of timers.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • The final product will result in the delay to be 5 minutes, I have set this to be low so I can see the values update faster. I did not know this would have an effect on the UI – SoloFeed Jan 05 '22 at 12:16
  • @SoloFeed I'm not sure what your point is. A timer can have much greater interval than 5 min. Note that they are usually limited to at minimum 1-55ms depending on type of timer and platform. – JonasH Jan 05 '22 at 12:21
0

To start a task about every 5 seconds, my advice would be to use class System.Timers.Timer. It is a Component, so it must be disposed when your form is disposed. Best way to do this is to add it to your form's property components.

private Timer CreateUpdateTimer()
{
    TimeSpan timerTime = TimeSpan.FromSeconds(5);
    Timer timer = new Timer(timerTime.TotalMilliSeconds);

    // Hook up the Elapsed event for the timer. 
    timer.Elapsed += OnTimedEvent;
    timer.AutoReset = true;
    timer.Enabled = true;
    return timer;
}

Consider to create a procedure with the TimeoutTime as TimeSpan parameter

// will be called every 5 seconds:
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
    this.UpdateValues(); // the procedure that updates your values
}

In your constructor:

public Form1()
{
    InitializeComponent();
    this.components.Add(this.CreateUpdateTimer());
}
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116