I'm writing an app to listen music and watch video. In app, I have these 2 pages: 1 to play the audio playback agent (APA) - Page A, and one to play the video (using MediaElement) - Page B. While WP8 has an bug with the APA ( which I've asked here here (which I found a solution - stop the APA twice after and before when the MediaElement plays and closes).
Problem is, at the A page, I used an DispatcherTimer tick every second to check the APA instance positon, and when I leave this page, I have a funtion to stop this DispatcherTimer .
Even that, if I navigated to and from page A for a several times, and then navigate to page B, after 1 Second the app thrown an exception at the if (BackgroundAudioPlayer.Instance.PlayerState == .... It means that the DispatcherTimer still tick ????? How can I force to stop this :(
I posted all code which related to the DispatcherTimer here :
public DetailSongPage()
{
InitializeComponent();
this.DataContext = App.Model;
BackgroundAudioPlayer.Instance.PlayStateChanged += APA_Instance_PlayStateChanged;
}
DispatcherTimer timer = null;
private void APA_Instance_PlayStateChanged(object sender, EventArgs e)
{
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
{
UpdateTracking();
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing || BackgroundAudioPlayer.Instance.PlayerState == PlayState.Paused)
{
UpdateTracking();
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
timer.Stop();
timer.Tick -= timer_Tick;
timer = null;
}
private void UpdateTracking()
{
sldTracking.Maximum = BackgroundAudioPlayer.Instance.Track.Duration.TotalMilliseconds;
tbMaxTime.Text = string.Format(#"{0:mm\:ss}",BackgroundAudioPlayer.Instance.Track.Duration);
// ^ these 2 lines update the UI for the slider and textblock
if (timer == null)
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += timer_Tick;
timer.Start();
}
}
private void timer_Tick(object sender, EventArgs e)
{
if (this.timer != null)
{
try
{
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing || BackgroundAudioPlayer.Instance.PlayerState == PlayState.Paused)
{
sldTracking.Value = BackgroundAudioPlayer.Instance.Position.TotalMilliseconds;
tbCurrentTime.Text = string.Format(#"{0:mm\:ss}", BackgroundAudioPlayer.Instance.Position);
}
}
catch
{
return;
}
}
}
You could implement the IDispose in your class, in this implementation, stop the timer.
Then call the Dispose whenever you leave the page.
In the Dispose, you should write something like:
timer?.Stop();
Related
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
I have the following code :
public partial class FereastraAlerta : UserControl
{
private static DispatcherTimer dispatcherTimer;
public FereastraAlerta()
{
InitializeComponent();
}
public void InitTimer()
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, Convert.ToInt32(textBox.Text), 0);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
CommandManager.InvalidateRequerySuggested();
Window.GetWindow(this).Show();
}
private void textBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key < Key.D0 || e.Key > Key.D9)
{
e.Handled = true;
}
}
private void btnOK_Click(object sender, RoutedEventArgs e)
{
Window.GetWindow(this).Close();
}
private void btnRemind_Click(object sender, RoutedEventArgs e)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if(Window.GetWindow(this).Title == "ANM")
config.AppSettings.Settings["nIntervalDeTimpReminderANM"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "CAS")
config.AppSettings.Settings["nIntervalDeTimpReminderCAS"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "MS")
config.AppSettings.Settings["nIntervalDeTimpReminderMS"].Value = textBox.Text;
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
Window.GetWindow(this).Hide();
InitTimer();
}
}
I open this window with the following :
if (data != strDataCurentaANM)
{
FereastraAlerta win1 = new FereastraAlerta();
Window window1 = new Window
{
Title = "ANM",
Content = win1,
Height = 300,
Width = 300
};
window1.Show();
win1.label.Content = textAvertizare + " " + data;
win1.textBox.Text = ConfigurationManager.AppSettings["nIntervalDeTimpReminderANM"];
strDataCurentaANM = data;
modifica = true;
}
This application check some websites and if the information changed it give me a warning, it have 3 warning windows at the moment and I got this problem:
I open 3 windows and I want them to remind me about the warning in 2 , 4 ,6 min, no problem until here, they will hide and will apear on the screen in that time. But if I try to modify the reminder again it will create another timer or something like this , I don't really understand the behavior.
Also the period that the window will apear it will be much shorter that the time inserted .https://i.stack.imgur.com/gkeu6.png
If I press ok the window should close and they does but the timer is somehow still alive and will try to show the window which I closed so it will generate this error https://i.stack.imgur.com/4dDzJ.png
Ok my guess is that whenever I hit the remind button it will create a new timer and the old one will still run(but this don't explain why sometimes all the windows will popup at once even if the reminder is not the same) and this is right:
What can I do in order to get rid of the old timer and let only the new one.
Also if you can give me any suggestions about how I should improve my code I'm listening .
If I press ok the window should close and they does but the timer is somehow still alive and will try to show the window which I closed so it will generate this error
Unsubscribe the dispatcherTimer_Tick and stop the timer before closing your window
private void btnOK_Click(object sender, RoutedEventArgs e)
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
dispatcherTimer.Stop();
}
Window.GetWindow(this).Close();
}
if I try to modify the reminder again it will create another timer
Same thing when you start a new timer
public void InitTimer()
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
dispatcherTimer.Stop();
}
Ok my guess is that whenever I hit the remind button it will create a
new timer and the old one will still run
Yes, definitely, each time you call InitTimer() you create a new timer. But also you didn't kill the old one, and it wont be garbage collected because it has an event subscription.
Thus each time it creates a new timer and they all tick whenever they should.
To avoid that, you have to be carefull with subscription in general. Whenever you write something like:
dispatcherTimer.Tick += dispatcherTimer_Tick;
You have to write also somewhere (usually in object cleanup):
dispatcherTimer.Tick -= dispatcherTimer_Tick;
Otherwise the event will still live. It is a really common error in C#, responsible for lots of memory leaks.
A quick workaround for you could be:
public void InitTimer()
{
if (dispatcherTimer != null)
{
dispatcherTimer.Tick -= dispatcherTimer_Tick;
}
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, Convert.ToInt32(textBox.Text), 0);
dispatcherTimer.Start();
}
Also if you can give me any suggestions about how I should improve my
code I'm listening
To be honest, this code is pretty bad, not testable and with bad names. To improve you craft I can suggest you to learn about the MVVM pattern and to read Clean Code.
Hope it helps.
What can I do in order to get rid of the old timer and let only the new one?
Try to stop the old timer before you start a new one:
private void btnRemind_Click(object sender, RoutedEventArgs e)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (Window.GetWindow(this).Title == "ANM")
config.AppSettings.Settings["nIntervalDeTimpReminderANM"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "CAS")
config.AppSettings.Settings["nIntervalDeTimpReminderCAS"].Value = textBox.Text;
else if (Window.GetWindow(this).Title == "MS")
config.AppSettings.Settings["nIntervalDeTimpReminderMS"].Value = textBox.Text;
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
Window.GetWindow(this).Hide();
//STOP the old timer here:
if (dispatcherTimer != null)
{
dispatcherTimer.Stop();
}
InitTimer();
}
DispatcherTimer is not executing code when I add conditions or try and start the timer outside the constructor.
The problem I am having is if I start the DispatchTimer in the constructor without condition my graphics layer adds before button Show_ClickStuff is clicked. If I don't start everything in the constructor and I place my if conditions in the Show_ClickStuff or in _timer_tick I can place Break Points on the code and watch the timer hit the lines of code but will not execute the lines of code. If I remove conditions code will execute. I have tried all kinds of variations and can not get this to work the way I need
using System.Windows.Threading;
.NET Framework 4.5
Code file: .xaml.cs
Silverlight 4
public Tool()
{
InitializeComponent();
_timer1 = new DispatcherTimer();
//_timer1.Start();
_timer1.Interval = TimeSpan.FromMilliseconds(4000);
_timer1.Tick += new EventHandler(_timer_tick);
}
private void Show_ClickStuff(object sender, RoutedEventArgs e)
{
ToggleStuff();
if (showStuff.IsChecked == true)
{
//Timer_Toggle(this, null);
//_timer1 = new DispatcherTimer();
//_timer1.Start();
//_timer1.Interval = TimeSpan.FromMilliseconds(4000);
//_timer1.Tick += new EventHandler(_timer_tick);
_timer1.Start();
}
else if (!_timer1.IsEnabled && showStuff.IsChecked == true)
{
_timer1.Start();
}
else
{
_timer1.Stop();
}
}
private void _timer_tick(object sender, EventArgs e)
{
_map.Layers.Remove(_stuffLayer);
GetStuff();
_map.Layers.Add(_stuffLayer);
}
I get List of websites I need to loop through and to spend on each certain amount of time. Looping needs to be asynchronous, because on each website music will be played, and that's the main point - to hear the music in that amount of time, and then to load another page and to listen to its music and so on. Also, form need to be available for user actions.
Code I've got so far is this:
public void playSound(List<String> websites)
{
webBrowser.Navigate(Uri.EscapeDataString(websites[0]));
foreach (String website in websites.Skip(1))
{
StartAsyncTimedWork(website);
// problem when calling more times
}
}
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void StartAsyncTimedWork(String website)
{
myTimer.Interval = 7000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
}
else
{
lock (myTimer)
{
if (this.myTimer.Enabled)
{
this.myTimer.Stop();
// here I should get my website which I need to search
// don't know how to pass that argument from StartAsyncTimedWork
}
}
}
}
One way to do this is as below.
Make websites a class field (if it isn't already), so the timer event handler can access this collection.
Add a field to keep track of the current index.
Add a field to prevent re-entrant calls to PlaySounds.
You're using a WinForms timer, which executes on the same thread as the form, so there's no need for InvokeRequired etc.
Some pseudo-code (warning, this is untested):
private bool isPlayingSounds;
private int index;
private List<String> websites;
private Timer myTimer;
private void Form1_Load()
{
myTimer = new System.Windows.Forms.Timer();
myTimer.Interval = 7000;
myTimer.Tick += new EventHandler(myTimer_Tick);
}
public void PlaySounds(List<String> websites)
{
if (isPlayingSounds)
{
// Already playing.
// Throw exception here, or stop and play new website collection.
}
else
{
isPlayingSounds = true;
this.websites = websites;
PlayNextSound();
}
}
private void PlayNextSound()
{
if (index < websites.Count)
{
webBrowser.Navigate(Uri.EscapeDataString(websites[index]));
myTimer.Start();
// Prepare for next website, if any.
index++;
}
else
{
// Remove reference to object supplied by caller
websites = null;
/ Reset index for next call to PlaySounds.
index = 0;
// Reset flag to indicate not playing.
isPlayingSounds = false;
}
}
private void myTimer_Tick(object sender, EventArgs e)
{
myTimer.Stop();
PlayNextSound();
}
Could any one help me to stop my timer in windows form C3 application? I added timer in form using designer and interval is set as 1000; I would like to do some actions after 5 seconds of waiting after button click. Please check the code and advise me. Problem now is I get MessageBox2 infinitely and never gets the timer stop.
static int count;
public Form1()
{
InitializeComponent();
timer1.Tick += timer1_Tick;
}
public void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
while(count>5)
{
....dosome actions...
}
}
private void timer1_Tick(object sender, EventArgs e)
{
count1++;
MessageBox.Show("Messagebox2");
if (count1 == 5)
{
//timer1.Enabled = false; timer1.Stop();
((System.Timers.Timer)sender).Enabled = false;
MessageBox.Show("stopping timer");
}
}
I would render the count useless and just use the timer 1 interval property and put your actions in the timer1_Tick event.
public void button1_Click(object sender, EventArgs e)
{
timer1.Interval = 5000;
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
MessageBox.Show("stopping timer");
// Your other actions here
}
You are incrementing count1 and checking count.
while(count1 > 5)
{
...dosome actions...
}
Which Timer do you use? Because C# supports class Timer from two different namespaces. One is from Forms, the other is from System.Timers. I would suggest you to use the other one - System.Timers.Timer.
Timer t = new Timer(20000); // created with 20seconds
t.Enabled = true; // enables firing Elapsed event
t.Elapsed += (s, e) => {
\\do stuff
};
t.Start();
In this short code you can see how the timer is created and enabled. By registering to the Elapsed event you explicitly say what to do after the time elapses. and this is done just once. Of course, there are some changes needed in case user clicks button before your limit is reached. But this is highly dependent on behavior of the action you demand.