I finally got rid of all the error messages as I attempted ways to find a control and enable it.
In the properties pane I disabled a button on mainwindow.
This code runs successfully, albeit annoyingly, because every second I have it give me another msgbox to show code is being triggered. But it is not enabling the button. I'm new to C# so it looks like arabic to me. In VB it would just be:
btnMyButton.Enabled = True
Here is my code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
}
public void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
DateTime today3am = now.Date.AddHours(3);
if (DateTime.Today == today3am.Date && now >= today3am)
{
MessageBox.Show("Code is being triggered");
btnMyButton.IsEnabled = true;
}
}
}
SOLVED: Response suggested this: (IT WORKED)
public void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
DateTime today3am = now.Date.AddHours(3);
if (DateTime.Today == today3am.Date && now >= today3am)
{
MessageBox.Show("Button Should Enable");
this.Dispatcher.Invoke(() => {
btnMyButton.IsEnabled = true;
});
}
}
When I copy and paste the code you've provided and run it, I get (as expected) an exception when trying to set the IsEnabled property:
The calling thread cannot access this object because a different thread owns it.
This is the standard "wrong thread" exception. You don't see the exception (apparently) because you're not running in a debugger. The Timer thread catches the exception and ignores it.
One way to fix the problem is to, as suggested by others, use Dispatcher.Invoke():
public void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
DateTime today3am = now.Date.AddHours(3);
if (DateTime.Today == today3am.Date && now >= today3am)
{
Dispatcher.Invoke(() => btnMyButton.IsEnabled = true);
}
}
However, since the problem is fundamentally caused by your use of the System.Timers.Timer class, it makes more sense to just use the correct timer class, System.Windows.Threading.DispatcherTimer:
public MainWindow()
{
InitializeComponent();
var myTimer = new DispatcherTimer();
myTimer.Tick += DisplayTimeEvent;
myTimer.Interval = TimeSpan.FromSeconds(1);
myTimer.Start();
}
public void DisplayTimeEvent(object source, EventArgs e)
{
DateTime now = DateTime.Now;
DateTime today3am = now.Date.AddHours(3);
if (DateTime.Today == today3am.Date && now >= today3am)
{
btnMyButton.IsEnabled = true;
}
}
Try to use Dispatcher. It may be that the GUI blocks up the change of controls from Code. The Dispatcher coordinates the access to an element from multiple threads:
this.Dispatcher.Invoke(() => {
btnMyButton.IsEnabled = true;
});
I think you need to set the property on the Dispatcher thread as you are not on the UI thread in the event handler
Related
I'm working on a basic audio player and I want to update some GUI elements based on the progression through the song.
Next to my Form I use an AudioPlayer class, which contains a ref on the created Form.
In the playAudio function I want to start a timer, which should call updateCurrTime, when elapsed. (For reference: I'm using NAudio)
The function calling the timer:
public bool playAudio()
{
if (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
waveOutDevice.Pause();
timer.Enabled = false;
return false;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Paused)
{
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Stopped)
{
initPlayer(mu_path);
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
return false;
}
And the function to update my Form with:
public void updateCurrTime()
{
while (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
}
}
I defined the timer like this:
timer = new Timer();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 100;
}
and the OnTimedEvent like this:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
self_ref.updateCurrTime();
}
I use a getter/setter structure for the label text:
public int curr_time
{
get { return Convert.ToInt32(this.l_t_curr.Text); }
set { this.l_t_curr.Text = value.ToString() + "s"; }
}
My problem is, that I'm getting an error, because the form is created on another thread. I did my research, but tbh, I didn't understand, how to implement BackGroundWorker or other solutions in my case.
With help of Julo's hint I was able to fix the issue.
public void updateCurrTime()
{
MethodInvoker methodInvokerDelegate = delegate ()
{ form1_ref.l_t_curr.Text = audioFileReader.CurrentTime.TotalSeconds.ToString(); };
//form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
//This will be true if Current thread is not UI thread.
if (form1_ref.InvokeRequired)
form1_ref.Invoke(methodInvokerDelegate);
else
methodInvokerDelegate();
}
To update GUI from another thread, you need to use Invoke or BeginInvoke.
Example:
private void GuiUpdate(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
GuiUpdate(sender, e);
});
return;
}
// put here GUI updating code
}
Difference between Invoke or BeginInvoke is:
Invoke stops execution of current thread until the called function ends,
when using BeginInvoke the starting thread continues without interruption.
Use Invoke when you need result from the function, or priority update. Otherwise it is better to use BeginInvoke.
I'm working on a windows forms app where I have several so called "services" that poll data from various services like Twitter, Facebook, Weather, Finance. Now each of my services has its individual polling interval setting so I was thinking I could implement a System.Windows.Forms.Timer for each of my services and set its Interval property accordingly so that each timer would fire an event at the preset interval that will cause the service to pull new data preferably async through a BackgroundWorker.
Is this the best way to do it? or will it slow down my app causing performance issues. Is there a better way of doing it?
Thanks!
You can do it with one Timer, just needs smarter approach to interval:
public partial class Form1 : Form
{
int facebookInterval = 5; //5 sec
int twitterInterval = 7; //7 sec
public Form1()
{
InitializeComponent();
Timer t = new Timer();
t.Interval = 1000; //1 sec
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
facebookInterval--;
twitterInterval--;
if (facebookInterval == 0)
{
MessageBox.Show("Getting FB data");
facebookInterval = 5; //reset to base value
}
if (twitterInterval == 0)
{
MessageBox.Show("Getting Twitter data");
twitterInterval = 7; //reset to base value
}
}
}
you do not really need BackgroundWorker, as WebClient class has Async methods.
so you may simply have one WebClient object for each of your "service" and use code like this:
facebookClient = new WebClient();
facebookClient.DownloadStringCompleted += FacebookDownloadComplete;
twitterClient = new WebClient();
twitterClient.DownloadStringCompleted += TwitterDownloadComplete;
private void FacebookDownloadComplete(Object sender, DownloadStringCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
string str = (string)e.Result;
DisplayFacebookContent(str);
}
}
private void OnFacebookTimer(object sender, ElapsedEventArgs e)
{
if( facebookClient.IsBusy)
facebookClient.CancelAsync(); // long time should have passed, better cancel
facebookClient.DownloadStringAsync(facebookUri);
}
I have been working on a program that has 3 classes of which 2 of the classes have timers that repeat at different intervals and once one "cycle" of the timer is done it raises an event with a string as return. The 3rd class subscribes to the events from the other two timer classes and prints them to screen. it works great!
But my issue is that it is printing them separately. Lets say that the first timer class runs and then raises "hello" every 2 minutes and the other class "dog" every second and every time an event is raised it prints the raised event to console. I would want it to instead print "hellodog" every second and store the value of the first timer(hello) in a private field or something so it still prints to screen even if the timer(the slower 2 minute timer) hasn't been fired. and when the 2 minute timer fires it updates the value to whatever the new one is and that new value get printed to screen until it fires again.
If it is confusing I will gladly clarify. its kind of hard to explain.
namespace Final
{
public class Output
{
public static void Main()
{
var timer1 = new FormWithTimer();
var timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer2_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString = e.Value;
//To something with 'theString' that came from timer 1
Console.WriteLine(theString);
}
static void timer2_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString2 = e.Value;
//To something with 'theString2' that came from timer 2
Console.WriteLine(theString2);
}
}
public abstract class BaseClassThatCanRaiseEvent
{
public class StringEventArgs : EventArgs
{
public StringEventArgs(string value)
{
Value = value;
}
public string Value { get; private set; }
}
//The event itself that people can subscribe to
public event EventHandler<StringEventArgs> NewStringAvailable;
protected void RaiseEvent(string value)
{
var e = NewStringAvailable;
if (e != null)
e(this, new StringEventArgs(value));
}
}
public partial class FormWithTimer : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer()
{
timer = new System.Timers.Timer(200000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (200000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick(object sender, EventArgs e)
{
...
RaiseEvent(gml.ToString());
}
}
public partial class FormWithTimer2 : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer2()
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick2); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick2(object sender, EventArgs e)
{
...
RaiseEvent(aida.ToString());
}
}
}
You can use the same event handler for both timers. And construct the output by identifying the senders. (Didn't test the code for syntax errors.)
private static string timer1Value = string.Empty;
private static string timer2Value = string.Empty;
private static FormWithTimer timer1;
private static FormWithTimer2 timer2;
public static void Main()
{
timer1 = new FormWithTimer();
timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
if (sender == timer1)
{
timer1Value = e.Value.ToString();
}
else if (sender == timer2)
{
timer2Value = e.Value.ToString();
}
if (timer1Value != String.Empty && timer2Value != String.Empty)
{
Console.WriteLine(timer1Value + timer2Value);
// Do the string concatenation as you want.
}
Correct me if I misunderstand the question, but it sounds like you want to coordinate your response to the two timer events (print "hellodog").
It seems to me that the easiest way to do this is to just use a single timer, and have the timer's event handler count the number of times the handler has been invoked to decide whether to take the once-per-second action, or also take the once-per-two-minutes action.
Since the slow timer is an exact multiple of your fast timer, you would set just one timer that triggers every second, and also do the 2-minute action every 120 invocations of the 1 second timer (120 seconds = 2 minutes).
I think I understand what you want and that is to synchronize the output of both timers. I am afraid there is no way to do it other than to slog through it. Set up a bunch of Boolean variables that track whether each event fired and whether the synchronized message was sent to the output.
This should do what you want.
public static void Main()
{
var timer1 = new FormWithTimer();
var timer2 = new FormWithTimer2();
var value1 = "";
var value2 = "";
Action writeValues = () => Console.WriteLine(value1 + value2);
timer1.NewStringAvailable += (s, e) =>
{
value1 = e.Value;
writeValues();
};
timer2.NewStringAvailable += (s, e) =>
{
value2 = e.Value;
writeValues();
};
Console.ReadLine();
}
Let me know if this is right. Cheers.
The second (quicker) timer should be the only one to print.
The first (slower) timer should only update a string which the second timer will use.
In the 'Output' class (you can put it before Main):
string string1;
and then:
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
string1 = e.Value;
}
static void timer2_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString2 = e.Value;
//To something with 'theString2' that came from timer 2
Console.WriteLine(string1 + theString2);
}
I found timer code from a web site and used it in my application and it works fine if I use the timer code inside a button_click handler, but I need to use the timer when I call a method, so I did copy and paste the same code from the button_click into the method but the timer always gives me zero. How do I fix it?
The following is the timer code.
public partial class Form1 : Form
{
//Timer decleration
DateTime startTime, StopTime;
TimeSpan stoppedTime;
bool reset;
bool startVariabl = true;
// The rest of the code..
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (startVariable) startMethod();
//
//The rest of the code...
}
private void startMethod()
{
//Timer
tmDisplay.Enabled = true;
// Reset display to zero
reset = true;
lblElapsed.Text = "00:00.00.0";
// Start timer and get starting time
if (reset)
{
reset = false;
startTime = DateTime.Now;
stoppedTime = new TimeSpan(0);
}
else
{
stoppedTime += DateTime.Now - StopTime;
}
}
private void tmDisplay_Tick(object sender, EventArgs e)
{
DateTime currentTime;
//Determine Ellapsed Time
currentTime = DateTime.Now;
// Display Time
lblElapsed.Text = HMS(currentTime - startTime - stoppedTime);
}
private string HMS(TimeSpan tms)
{
//Format time as string; leaving off last six decimal places.
string s = tms.ToString();
return (s.Substring(0, s.Length - 6));
}
I am new C# learner.
as keyboardP suggested in a comment you should add this line:
tmDisplay.Tick += new EventHandler(tmDisplay_Tick);
usually Tick handler is set once (unless you need to switch it to other callback or nullify it for some reason) so I would add it in your Form constructor assuming that timer is already created then or after timer initialization if you do it in some method.
With reference to the Software Project I am currently working on.
I have the below methods that basically move a canvas with a Timer:
DispatcherTimer dt = new DispatcherTimer(); //global
public void Ahead(int pix)
{
var movx = 0;
var movy = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler((object sender, EventArgs e) =>
{
if (movx >= pix || movy >= pix)
{
dt.Stop();
return;
}
Bot.Body.RenderTransform = new TranslateTransform(movx++, movy++);
});
dt.Start();
}
public void TurnLeft(double deg)
{
var currAngle = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (currAngle <= (deg - (deg * 2)))
{
dt.Stop();
}
Bot.Body.RenderTransform = new RotateTransform(currAngle--, BodyCenter.X, BodyCenter.Y);
});
dt.Start();
}
Now, from another library, these methods are called like such:
public void run()
{
Ahead(200);
TurnLeft(90);
}
Now of course, I want these animations to happen after another, but what is happening is that the dt.Tick event handler of the DispatchTimer is being overwritten when the second method (in this case, TurnLeft(90)) is invoked and thus, only the second method gets executed as it should.
I need to create some sort of queue that will allow me to push and pop methods to that queue so that dt (the DispatchTimer timer) executes them one by one...in the order they are in the 'queue'
Any way I can go about doing this ? Am I on the right track here, or completely off course?
When you call Invoke() or BeginInvoke() on the Dispatcher, the operation will be queued up and run when the thread associated with the Dispatcher is free. So instead of using the Tick event, use the overload of Dispatcher.Invoke that takes a Timespan.
I have fixed this problem by myself. What I did was create a global Queue of type Delegate and instead of executing the methods directly, I add them to this queue.
Then I would have a separate thread in the constructor that will dequeue methods one by one and executing them:
Queue<TimerDelegate> eventQueue = new Queue<TimerDelegate>();
public Vehicle(IVehicle veh, Canvas arena, Dispatcher battleArenaDispatcher)
{
DispatcherTimer actionTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
actionTimer.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (IsActionRunning || eventQueue.Count == 0)
{
return;
}
eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
});
actionTimer.Start();
}
public void TurnRight(double deg)
{
eventQueue.Enqueue((TimerDelegate)delegate(DispatcherTimer dt)
{
IsActionRunning = true;
var currAngle = 0;
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
lock (threadLocker)
{
if (currAngle >= deg)
{
IsActionRunning = false;
dt.Stop();
}
Rotator_Body.Angle++;
currAngle++;
}
});
dt.Start();
});
}