Using ThreadPoolTimer with Background Audio UWP - c#

I am making a UWP app using the BackgroundAudioTask. My app is working very well. Now I want to add in a TextBlock the Current position of the Audio played.
I was doing this method before implementing the audio Task:
private TimeSpan TotalTime;
private DispatcherTimer timerRadioTime;
private void radioPlayer_MediaOpened(object sender, RoutedEventArgs e)
{
TotalTime = radioPlayer.NaturalDuration.TimeSpan;
// Create a timer that will update the counters and the time slider
timerRadioTime = new DispatcherTimer();
timerRadioTime.Interval = TimeSpan.FromSeconds(1);
timerRadioTime.Tick += TimerRadioTime_Tick;
timerRadioTime.Start();
}
private void TimerRadioTime_Tick(object sender, object e)
{
//Check if the audio finished calculate it's total time
if (radioPlayer.NaturalDuration.TimeSpan.TotalSeconds > 0)
{
if (TotalTime.TotalSeconds > 0)
{
// Updating timer
TimeSpan currentPos = radioPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
radioTimerBlock.Text = currentTime;
}
}
}
When I implemented the Background Task it gave me an Exception. After researching I saw a suggestion of using ThreadPoolTimer instead of dispatcherTimer.
I tried writing this code (following this solution: Clock program employing ThreadPoolTimer C# uwp)
ThreadPoolTimer timer;
// for displaying time only
private void CurrentPlayer_MediaOpened(MediaPlayer sender, object args)
{
_clockTimer_Tick(timer);
}
private async void _clockTimer_Tick(ThreadPoolTimer timer)
{
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
// Your UI update code goes here!
if (CurrentPlayer.NaturalDuration.TotalSeconds > 0)
{
TimeSpan currentPos = CurrentPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
CurrentPosition.Text = currentTime;
}
});
}
This is obviously not working. The app enters the method without updating my UI. I really don't understand what timer should be. Any Idea on how to make it run?

Solution:
ThreadPoolTimer timer;
// for displaying time only
private void CurrentPlayer_MediaOpened(MediaPlayer sender, object args)
{
timer = ThreadPoolTimer.CreatePeriodicTimer(_clockTimer_Tick, TimeSpan.FromSeconds(1));
}
private async void _clockTimer_Tick(ThreadPoolTimer timer)
{
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
// Your UI update code goes here!
if (CurrentPlayer.NaturalDuration.TotalSeconds < 0)
{
TimeSpan currentPos = CurrentPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
CurrentPosition.Text = currentTime;
}
});
}

Related

Anyone have a good idea for a basic timer in C#?

Does anyone have a good idea for a timer? I've tried using the stopwatch but I must have done something wrong, I simply wish to have a int value go up once per second and have to ability to reset it.
This is my failed piece of code:
//Timer systemet
Stopwatch Timer = new Stopwatch();
Timer.Start();
TimeSpan ts = Timer.Elapsed;
double seconds = ts.Seconds;
//interval
if(seconds >= 8)
{
Text = Text + 1;
Timer.Stop();
}
I see you've tagged this question with XNA and MonoGame. Typically, in game frameworks like this you don't use typical timers and stopwatches.
In MonoGame you would normally do something like this:
private float _delay = 1.0f;
private int _value = 0;
protected override void Update(GameTime gameTime)
{
var deltaSeconds = (float) gameTime.ElapsedGameTime.TotalSeconds;
// subtract the "game time" from your timer
_delay -= deltaSeconds;
if(_delay <= 0)
{
_value += 1; // increment your value or whatever
_delay = 1.0f; // reset the timer
}
}
Of course, this is the absolute simplest example. You can get more fancy and create a custom class to do the same thing. This way you can create multiple timers. There are examples of this in MonoGame.Extended which you're welcome to borrow the code from.
Easiest way is to use System.Timers.Timer.
Example:
using System.Timers;
class Program
{
static void Main(string[] args)
{
Timer t = new Timer(_period);
t.Elapsed += TimerTick;
t.Start();
}
static void TimerTick(Object source, ElapsedEventArgs e)
{
//your code
}
}
If you need more variable Timer, you can use System.Threading.Timer (System.Timers.Timer is basically wrapper around this class).
Example:
using System.Threading;
class Program
{
static void Main(string[] args)
{
Timer t = new Timer(TimerTick, new AutoResetEvent(false), _dueTime, _period);
}
static void TimerTick(Object state)
{
//your code
}
}

WPF / C# (No MVVM) Timer with Countdown Clock

Good day all,
I'm struggling with a coding problem and I need some help. My process requires a 300 second (5 min) timer that fires an event to refresh a Grid Control. That works just fine. The problem is, I need to countdown the 5 mins/300 seconds to the user so they know the next fresh happens in "X" seconds. The goal is to countdown, refresh, and show the user the next refresh.
All code samples below are examples of things I tried.
I have code that works for the refresh, but something strange happens after the first execution. The timer counts down to 0, refreshes, and then restarts at 300 seconds again (good), but each tick down flashes a second timer behind it. So I see 300, 299, 298, ... and then another 300, 299, 298, ...; therefore, it looks like 300, 299, 298, 300, 297, 299, 296, 298, etc. It's nauseating to watch. Let alone trying to watch 20 minutes in...
My 300-second timer is a System.Timers.Timer int eh below example (Reminder, this works):
public partial class MasterControl
{
private Timer _t;
public MasterControl()
{
InitializeComponent();
}
public void Dispose()
{
_t?.Dispose();
_handle?.Dispose();
}
private void Master_OnLoaded(object sender, RoutedEventArgs e)
{
var fd = new FillMaster();
GridMaster.ItemsSource = fd.GridPopulate("TblName", App.UserName);
_t = new Timer();
_t.Elapsed += OnTimedEvent;
_t.Interval = 300000;
_t.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Dispatcher.Invoke(() =>
{
try
{
var fd = new FillMaster();
GridMaster.ItemsSource = fd.GridPopulate("TblName", App.UserName);
}
catch (SqlException)
{
/* swallow */
}
});
}
}
What I tried to do was add a countdown that fills a label.
I added
private TimeSpan _time;
private DispatcherTimer _timer;
And I adjusted the OnTimedEvent code to the below example and it didn't work. This is where it started to double up on refresh. I tried GC to see if that would work. No dice.
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Dispatcher.Invoke(() =>
{
try
{
var fd = new FillMaster();
GridMaster.ItemsSource = fd.GridPopulate("IntakeCheckList", App.UserName);
TbCountDown.Content = "";
_time = TimeSpan.FromSeconds(60);
_timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Normal, delegate
{
TbCountDown.Content = _time.ToString("c");
if (_time == TimeSpan.Zero)
{
_timer.Stop();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
_time = _time.Add(TimeSpan.FromSeconds(-1));
}, Application.Current.Dispatcher);
_timer.Start();
}
catch (SqlException)
{
/* swallow */
}
});
}
I also found some code that casued the same problem.
private void Countdown(int count, TimeSpan interval, Action<int> ts)
{
var dt = new DispatcherTimer {Interval = interval};
dt.Tick += (_, a) =>
{
if (count-- == 0)
dt.Stop();
else
ts(count);
};
ts(count);
dt.Start();
}
Then I added the following to the to OnTimedEvent
Countdown(30, TimeSpan.FromSeconds(5), cur => TbCountDown.Content = cur.ToString());
As seen here
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Dispatcher.Invoke(() =>
{
Countdown(30, TimeSpan.FromSeconds(5), cur => TbCountDown.Content = cur.ToString());
try
{
var fd = new FillMaster();
GridMaster.ItemsSource = fd.GridPopulate("IntakeCheckList", App.UserName);
}
catch (SqlException)
{
/* swallow */
}
});
}
This also failed with the exact same problem.
Ultimately, is there a way to get the countdown from the System.Timers.Timer or something else you can help me with?
Thank you!
You don't actually need more than a simple DispatcherTimer and a DateTime that is cyclically reset to the current time + 300 seconds.
public partial class MainWindow : Window
{
private readonly DispatcherTimer timer = new DispatcherTimer();
private DateTime endTime;
public MainWindow()
{
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(OnTimerTick);
timer.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
var now = DateTime.Now;
if (endTime < now)
{
endTime = now.AddSeconds(300);
}
label.Content = (endTime - now).ToString(#"mm\:ss");
}
}

How to check if mouse doesn't move in WPF?

I want to write a function that returns true if mouse doesn't move for given amount of time.
I've tried Mouse.GetPosition() but it keeps returning the same coordinates.
private bool MouseNotMoves(int time)
{
Point currentMouseCoord = Mouse.GetPosition(this);
TimeSpan timeSpan = new TimeSpan(0,0,0,0,0);
DateTime start = DateTime.Now;
while (timeSpan.TotalMilliseconds <= time)
{
System.Threading.Thread.Sleep(100);
timeSpan = DateTime.Now - start;
Point newMouseCoord = Mouse.GetPosition(this);
if (currentMouseCoord != newMouseCoord)
start = DateTime.Now;
currentMouseCoord = newMouseCoord;
}
return true;
}
I want to get mouse coordinates when calling the function and then again after some time and compare them, the problem is that the function always returns true after exact given time. When i try debugging the mouse coordinates never change even if I move the mouse.
I'm quite new to programing so if there's a better way to achieve this I'd really appreciate any suggestions.
This is an example of what I meant in the comments by inverting the logic. It's quite dirty, but I believe it highlights the idea.
private bool _mouseMoved;
private async void Button_Click(object sender, RoutedEventArgs e)
{
MyTextBlock.Text = (await IfMouseMoved(TimeSpan.FromSeconds(4))).ToString();
}
private async Task<bool> IfMouseMoved(TimeSpan timeSpan)
{
MouseMove += MainWindow_MouseMove;
try
{
_mouseMoved = false;
await Task.Delay(timeSpan);
return _mouseMoved;
}
finally
{
MouseMove -= MainWindow_MouseMove;
}
}
private void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
_mouseMoved = true;
}
UPD. The same solution can be written concisely using local function for the callback:
private async Task<bool> IfMouseMoved(TimeSpan timeSpan)
{
var mouseMoved = false;
void MouseMovedCallback(object sender, MouseEventArgs e)
{
mouseMoved = true;
}
MouseMove += MouseMovedCallback;
try
{
await Task.Delay(timeSpan);
return mouseMoved;
}
finally
{
MouseMove -= MouseMovedCallback;
}
}
The following code will work only when the cursor is in your application.
XAML
<Grid MouseMove="Grid_MouseMove">
</Grid>
Code Behind
System.Timers.Timer timer;
public YourConstructor()
{
InitializeComponent();
// Create a timer with a ten second interval.
timer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
timer.Elapsed += OnTimedEvent;
// Raise the Elapsed event only once.
timer.AutoReset = false;
// Start the timer.
timer.Start();
}
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
{
// Mouse has not moved for 10s.
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
// Reset the timer.
if(timer.Enabled)
{
timer.Stop();
}
timer.Start();
}
More info about System.Timers.Timer

How to delay a timer from running or start it based on current date time

I have console application am using as demo to an App, it prints "hello", based on the timespan its expected to alert the user. when its not yet the timespan, i want to delay the app from printing hello and resume when its time.
public static async void timeCounter(int delae)
{
//This is suppose to cause a delay but it instead initiate the
//TimerOperation_Tick method.
await Task.Delay(delae);
// timer countdown
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 1000; // 1 second
timer.Elapsed += new ElapsedEventHandler(TimerOperation_Tick);
timer.Start();
if (obj.SubmissionCheck == true)
{
timer.Stop();
}
}
/// the event subscriber
private static void TimerOperation_Tick(object e, ElapsedEventArgs args)
{
if (timeFrame != 0)
{
Console.WriteLine("hi" + timeFrame);
timeFrame --;
if (timeFrame < 1)
{
obj.SubmissionCheck = true;
nt.Remove(obj);
startNotification();
}
}
}
Try setting timer.Enabled = false; This will prevent the timer ticks from occurring.

Countdown Timer in a strip status label c#

My program has a parameter that starts up the winform and waits x number of seconds before it runs a function. Currently I am using Thread Sleep for x seconds and then the function runs. how can I add a timer in the strip status label?
so that it says: x Seconds Remaining...
Instead of blocking thread execution, simply call your method when required timeout passes. Place new Timer to your form, and set it's Interval to 1000. Then subscribe to timer's Tick event and calculate elapsed time in event handler:
private int secondsToWait = 42;
private DateTime startTime;
private void button_Click(object sender, EventArgs e)
{
timer.Start(); // start timer (you can do it on form load, if you need)
startTime = DateTime.Now; // and remember start time
}
private void timer_Tick(object sender, EventArgs e)
{
int elapsedSeconds = (int)(DateTime.Now - startTime).TotalSeconds;
int remainingSeconds = secondsToWait - elapsedSeconds;
if (remainingSeconds <= 0)
{
// run your function
timer.Stop();
}
toolStripStatusLabel.Text =
String.Format("{0} seconds remaining...", remainingSeconds);
}
You can use a Timer:
public class Form1 : Form {
public Form1(){
InitializeComponent();
t = new Timer {Interval = 1000};
t.Tick += Tick;
//try counting down the time
CountDown(100);
}
DateTime start;
Timer t;
long s;
public void CountDown(long seconds){
start = DateTime.Now;
s = seconds;
t.Start();
}
private void Tick(object sender, EventArgs e){
long remainingSeconds = s - (DateTime.Now - start).TotalSeconds;
if(remainingSeconds <= 0) {
t.Stop();
toolStripStatusLabel1.Text = "Done!";
return;
}
toolStripStatusLabel1.Text = string.Format("{0} seconds remaining...", remainingSeconds);
}
}

Categories