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);
}
Related
I have a site that is extremely basic and will only ever consist of a single integer.
However the integer will actively change, I want to add onto my existing application to display what this integer is in real time.
-I've tried using a Timer and WebClient however if I put the code under InitializeComponent() the form will never load.
-Also if I put the code in Form1_Load the form will never load.
-I was successful in getting the number to display in real time by putting the code under a button_click event, but I want this code to begin as soon as the form load.
-Also when the button was first clicked the first timer sequence the label would display lat (unsure what this means)
-After the button was pressed and the timer loop began the app breaks, the number will update properly, but you cannot use any other functionality of the app, you can not move the window, you cannot close the app, etc..
private void timer_Tick(object sender, EventArgs e)
{
Timer timer = (Timer)sender;
this.Visible = false;
timer.Stop();
this.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
int c = 5;
while (c == 5)
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 5000;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
}
Please help me no clue what I am doing wrong here
I managed to fix my issue by using the following code if anyone ever has a similar question:
private void test()
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
}
}
int i = 1;
private void timer1_Tick(object sender, EventArgs e)
{
i += 1;
if (i >= 199)
{
i = 1;
timer1.Stop();
timer1.Start();
}
test();
}
timer1 was added to the winform from the toolbox, and is set to enabled with an interval of 200
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
Got a problem at the moment, I'm using a timer to do animations and I want to be able to decide where to start using Start and Stop integers as i'll show below.
private void Button1_Click(object sender, EventArgs e)
{
AnimateKey(0,100);
}
private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Enabled = true;
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
lock (myTimer)
{
int StartingFrame = Start;
int StopFrame = Stop;
etc...etc..
}
}
Now my problem is that I want to pass the values 0 and 100 to the Timer Tick event but I have no idea on how to go about doing it.
How can I get the Integers 0 and 100 from the button click to the timer tick ?
Just use a lambda when defining the tick event handler to close over the parameters you need:
private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += (s, args) => myTimer_Tick(Start, Stop);
myTimer.Enabled = true;
myTimer.Start();
}
private void myTimer_Tick(int Start, int Stop)
{
//Do stuff
}
Also note that the Tick event of the Timer that you're using will be fired in the UI thread, so there is no need for a lock; the code is already synchronized.
use a class with all info:
public class TimerInfo
{
public int Start;
public int Stop;
}
store an instance in timer's Tag
myTimer.Tag = new TimerInfo { Start = 0, Stop = 100 };
inside the eventhandler you access this info
myTimer = (Timer)sender;
TimerInfo ti = (TimerInfo)myTimer.Tag;
Somewhat hard to understand what you mean, but let's give it a try.
If you ment you want to pass the integers start and stop to the function TimerTick, you probably dont understand the EventArgs parameter. EventArgs is ment to store the arguements which are relevant to your scenario - and the solution is simple.
class myTimerEventArgs:EventArgs // Declaring your own event arguements which you want to send
{
public int start{get;set;}
public int stop {get;set;}
/*Constructor, etc...*/
}
...
//Making the call inside another class:
myTimer_Tick(this,new myTimerEventArgs(0,100);
However, I could be misunderstanding you; If are talking about counting the ticks until it reaches 100 ticks (/intervals), the solution is a simple function added to the event, which would probably look like this:
int Count = 0;
...
private void Counter(object sender, EventArgs e)
{
Count++;
}
...
private void AnimateKey(int Start, int Stop)
{
myTimer.Interval = 5;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Tick += new EventHandler(Counter);
myTimer.Enabled = true;
myTimer.Start();
while(Count!=100);
myTimer.Stop();
}
Hope I helped, have a nice day :)
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();
}
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);
}