I refactored my WPF code recently and now my DispatcherTimer stopped firing. I checked other similar posts here, but they all seemed to be problems with having the wrong dispatcher thread set, which I tried...
My code looks like this:
class MainWindow : Window
{
private async void GoButton_Click(object sender, RoutedEventArgs e)
{
Hide();
m_files = new CopyFilesWindow();
m_files.Show();
m_dispatcherTimer = new DispatcherTimer();
m_dispatcherTimer.Tick += dispatcherTimer_Tick;
m_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 250);
m_dispatcherTimer.Start();
await SomeLongRunningTask();
m_files.Hide();
Show();
}
(The current class is my main Window object, which I hide for the duration of file copying. CopyFilesWindow is a simple Xaml window that contains controls I modify...CopyFilesWindow does absolutely nothing itself.)
Basically, I await a long running task (copying a bunch of large files), and my DispatcherTimer is supposed to update the progress in dispatcherTimer_Tick. However, I set a breakpoint on that function and it doesn't get hit.
I have also tried setting the Dispatcher with the constructor like so:
m_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal, m_files.Dispatcher);
m_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal, this.Dispatcher);
But neither of these things change the behavior...it still doesn't fire.
What am I doing wrong here?
The DispatcherTime runs on the ... Dispatcher thread. Which is stuck waiting SomeLongRunningTask() to finish.
Indeed, when you press the button Go, it is the dispatcher thread which executes GoButton_Click. Thus, you should never make a method called by UI (the dispatcher thread) async.
private void GoButton_Click(object sender, RoutedEventArgs e)
{
Hide();
m_files = new CopyFilesWindow();
m_files.Show();
m_dispatcherTimer = new DispatcherTimer();
m_dispatcherTimer.Tick += dispatcherTimer_Tick;
m_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 250);
m_dispatcherTimer.Start();
SomeLongRunningTask.ContinueWith(() =>
{
// Executes this once SomeLongRunningTask is done (even if it raised an exception)
m_files.Hide();
Show();
}, TaskScheduler.FromCurrentSynchronizationContext()); // This paramater is used to specify to run the lambda expression on the UI thread.
}
Related
I am currently somewhat new to c#/wpf (and coding in general). I decided to start another project, being a custom made "task manager" of sorts.
(While I use binding, this is NOT a MVVM project, so all answers welcome)
If you have ever opened task manager, you know that one of the main helpful tools it provides is a updating view of CPU/RAM/Whatever usage. Telling the user what percent of the resource they are using.
My problem is not getting the CPU percentage. I am unsure on how to refresh the text property for CPU load in the UI efficiently.
My first thought was that I should create a Background worker (which is probably correct) to separate the thread loads. However, I can't seem to wrap my mind on the solution to implement the background workers in a useful way.
The code is currently set up in this fashion:
When page is loaded, public BgWrk creates a new instance of it self.
Adds task to be called when ran.
BgWrk is ran.
New instance of method to be called is made.
Dispatcher is invoked on main thread to update UI.
Invoke consists of setting public string PerCpu (bound in other class, using INotifyPropertyChanged & all) on the return value of "grabber"'s CpuPerUsed.
BgWrk disposed.
Program loops (this is most likely the problem).
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
BgWrk = new BackgroundWorker();
BgWrk.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
BgWrk.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
CpuInfoGrabber grabber = new CpuInfoGrabber();
Application.Current.Dispatcher.Invoke(new Action (() => Bnd.PerCpu = grabber.CpuPerUsed()));
BgWrk.Dispose();
}
}
Again the code works, but it is WAY to slow due to the load of retrieving all of that data. Any suggestions on how to make this work well are appreciated!
Thanks
Instead of looping you could use a timer to periodically poll for the CPU usage.
class Test
{
private System.Timers.Timer _timer;
public Test( )
{
_timer = new System.Timers.Timer
{
// Interval set to 1 millisecond.
Interval = 1,
AutoReset = true,
};
_timer.Elapsed += _timer_Elapsed;
_timer.Enabled = true;
_timer.Start( );
}
private void _timer_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
{
// This handler is not executed on the gui thread so
// you'll have to marshal the call to the gui thread
// and then update your property.
var grabber = new CpuInfoGrabber();
var data = grabber.CpuPerUsed();
Application.Current.Dispatcher.Invoke( ( ) => Bnd.PerCpu = data );
}
}
I'd use Task.Run instead of a BackgroundWorker in your case:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
//Keep it running for 5 minutes
CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(hours: 0, minutes: 5, seconds: 0));
//Keep it running until user closes the app
//CancellationTokenSource cts = new CancellationTokenSource();
//Go to a different thread
Task.Run(() =>
{
//Some dummy variable
long millisecondsSlept = 0;
//Make sure cancellation not requested
while (!cts.Token.IsCancellationRequested)
{
//Some heavy operation here
Thread.Sleep(500);
millisecondsSlept += 500;
//Update UI with the results of the heavy operation
Application.Current.Dispatcher.Invoke(() => txtCpu.Text = millisecondsSlept.ToString());
}
}, cts.Token);
}
Let's see if anyone can explain me this behaviour and maybe how can I solve this. I have a WPF app, and in my ViewModel I have a DispatcherTimer. In that ViewModel i have a command to show a modal window, something like this:
private void ShowWindowCommandExecuted()
{
wnNewWindow window = new wnNewWindow();
window.ShowDialog();
}
When i call this Command from a button, the new window is shown and the DispatcherTimer keeps running in the background. So far so good. The problem is when i try to show the window from the DispatcherTimer like this:
DispatcherTimer timerInstrucciones;
timerInstrucciones = new DispatcherTimer();
timerInstrucciones.Interval = TimeSpan.FromMilliseconds(1000);
timerInstrucciones.Tick += (s, e) =>
{
wnNewWindow window = new wnNewWindow();
window.ShowDialog();
};
timerInstrucciones.Start();
In this case, the new window is also shown, but as long it is visible, the DispatcherTimer stops "ticking". I understand the DispatcherTimer runs in the UI thread, but why it behaves in a different way in this case?
Generally, ShowDialog is a modal dialog that will block the calling thread, and show the dialog. It will also block interaction with the parent/owning window too.
As long as you close the Modal Dialog, the UI-Thread is blocked. Because its a DispatcherTimer, it belongs to the Window's Dispatcher and runs in the same thread. So if this thread is blocked, the DispatcherTimer stops running.
UPDATE BASED ON YOUR COMMENTS:
I haven't went through any documentation on this, but the basic difference would be DispatcherTimer will run Synchronously and not in Asynchronous way.
Won't block the Dispatcher:
timerInstrucciones.Tick += (s, e) =>
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
wnNewWindow mn = new wnNewWindow();
mn.ShowDialog();
}));
};
Will block the Dispatcher:
timerInstrucciones.Tick += (s, e) =>
{
this.Dispatcher.Invoke(new Action(() =>
{
wnNewWindow mn = new wnNewWindow();
mn.ShowDialog();
}));
};
Since, Dispatcher will Invoke the Event on every n seconds, Event cannot be called anymore, if the thread got blocked for any operation inside the calling Event .
am used the below code for automatically call for reporting.But after 30seconds the application hangs(i.e UI is freeze)for several seconds.
How to solve this without hanging problem.
below is my code
System.Windows.Threading.DispatcherTimer dispatcherTimer2 = new
System.Windows.Threading.DispatcherTimer();
dispatcherTimer2.Tick += new EventHandler(dispatcherTimer2_Tick);
dispatcherTimer2.Interval = new TimeSpan(0, 0, 30);
dispatcherTimer2.Start();
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
automaticreportfunction();
}
After 30seconds it hangs the application,how to solve this
The DispatcherTimer runs in a Background-Tread. All methods in the Tick-Event are running in the UI-Tread. That's why your application will freezer. You will have to move your automaticreportfunction() to a background-thread in order to keep your UI alive.
Therefor you have several opportunities. Personally I prefer the usage of a BackgroundWorker (I know that there a other ways, but I like this one most). So in your Tick-Event you can do something like:
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
DispatcherTimer dispatcherTimer = (DispatcherTimer)sender;
dispatcherTimer.Stop();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (bs, be) => automaticreportfunction();
backgroundWorker.RunWorkerCompleted += (bs, be) => dispatcherTimer.Start();
backgroundWorker.RunWorkerAsync();
}
I would also stop the timer when you enter the tick-method and restart it again after your automaticreportfunction finished because otherwise you could enter the method again while your execution still runs.
I have an app which uses DispatcherTimer to manage time, countdown things etc. and I do have multiple counters turned on while app is open. But the time is a little bit delayed, I'd say about 3-5 seconds per minute. This is part of the code I'm using:
DispatcherTimer ob = new DispatcherTimer();
ob.Interval = new TimeSpan(0, 0, 1);
private void bob_Click(object sender, RoutedEventArgs e) //Button starting countdown
{
ob.Start();
tikOb = 140;
ob.Tick += new EventHandler(ob_Tick);
}
void ob_Tick(object sender, EventArgs e)
{
tob.Text = tikOb.ToString();
if (tikOb > 0)
{
tikOb--;
}
else
{
ob.Stop();
tob.Text = "STOPPED";
ob.Tick -= new EventHandler(ob_Tick);
}
//Between these there is a code which is irrelevant in this case.
private void stopob_Click(object sender, RoutedEventArgs e) //Button breaking countdown
{
ob.Tick -= new EventHandler(ob_Tick);
ob.Stop();
tob.Text = "ON";
{
Can anyone tell me why is this happening? Did I do anything wrong inside the code? Oh, I also have another one in the code which uses different variables etc. It's completely separated. Thanks in advance!
The DispatcherTimer is executed on the UI thread. If the UI thread is currently busy doing other work for more than the interval, it will delay the execution of the Timer event and invoke it once it is free of prior work. If you need more precise scheduling, you should go for a time than runs in the background, like System.Threading.Timer or Task.Delay which can be awaited, if you're using .NET 4.5 and above. If you use the Timer and then invoke UI thread logic, you will have to remember to marshal back work to the UI thread.
I've been trying to get the logic right for my timer and backgroundworker thread. Granted I don't fully understand the whole system despite all my reading. the following are excerpts of code concerned:
My polling button :
private void pollStart_Click(object sender, EventArgs e)
{
tst_bgw = new BackgroundWorker();
//mandatory. Otherwise will throw an exception when calling ReportProgress method
tst_bgw.WorkerReportsProgress = true;
//mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
tst_bgw.WorkerSupportsCancellation = true;
tst_bgw.DoWork += tst_bgw_DoWork;
tst_bgw.ProgressChanged += tst_bgw_ProgressChanged;
tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted;
tst_bgw.RunWorkerAsync();
}
which I think is right so far
my Background worker thread:
private void tst_bgw_DoWork(object source, DoWorkEventArgs e)
{
m_timer = new System.Timers.Timer();
m_timer.Interval = 1000;
m_timer.Enabled = true;
m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
if (tst_bgw.CancellationPending)
{
e.Cancel = true;
return;
}
}
and the elapsed tier event code:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (powerVal > 3250)
{
m_timer.Stop();
tst_bgw.CancelAsync();
}
else
{
string pow;
int progressVal = 100 - ((3250 - powerVal) / timerVal);
uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it.
pow = construct_command("power", powerVal);
sData = Encoding.ASCII.GetBytes(pow);
if (active_connection)
try
{
m_sock.Send(sData);
Array.Clear(sData, 0, sData.Length);
tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK.
m_sock.Send(time_out_command);
tstDel(ref unit_Output);
tst_bgw.ReportProgress(progressVal);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
tst_bgw.ReportProgress(powerVal, progressVal);
powerVal = powerVal + pwrIncVal;
}
I'd just like to know a few other things; am I using the right timer (not that I think it should matter greatly but it was suggested that this might be the best timer for what I want to do) and canI really modify UI elements in the DoWork method only through delegates and if yes are there sepcial considerations to doing so.
Sorry about the long posting and thank you for your time.
There is lots wrong with this code.
1) You aren't disposing of your background worker. BackgroundWorkers must be disposed of after use. They are designed to be used as winforms components and would normally be added to a window via the designer. This will ensure it is created with the form and disposed of when the form is.
2) All you are doing in your dowork method is creating a new timer and running it. There is no point of doing this in a background worker because it will happen so quickly anyway.
3) You will recreate the timer every time you run the background worker again. But you aren't ever stopping or disposing of the old timer, you are just overwriting the member.
I recommend you get rid of the BackgroundWorker completely and just use a timer. Create the timer in the forms constructor and make sure you dispose of it in the forms dispose method. (Or use the designer to add it to the form). In the pollstart_click method just start the timer. (If you have a poll stop method, you can stop the timer in that)
You don't need both a BackgroundWorker and a Timer to accomplish your goal. From what you have posted it looks like you want to have the user click a button which starts a polling process that quits at a certian point.
Your polling model really suggests a timer would work just fine.
If you use a Timer I would Initialize the timer after the InitializeComponent() call with something like
private void InitializeTimer()
{
this.timer = new Timer();
int seconds = 1;
this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds
this.timer.Tick += new EventHandler(timer_Tick);
// don't start timer until user clicks Start
}
The button_click will simply
private void button_Click(object sender, EventArgs e)
{
this.timer.Start();
}
Then on the timer_Tick you will need to do your polling and you should be able to update your UI from there if the timer is on the UI thread like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set progressbar, label, etc with value from method above
}
}
However if the timer thread is not on the same thread as the UI you well get an exception while trying to update the UI. In that case you can use the Invoke that DataDink mentions and do something like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set a label with Invoke
mylabel.Invoke(
new MethodInvoker( delegate { mylabel.Text = "some string"; } )
);
}
}
Given the code you posted you really didn't need to do both a BackgroundWorker and a Timer, but I have had instances where I have used a BackgroundWorker to do work when a timer is called so that I could have a timer update UI periodically and have a manual button to Refresh the UI. But I wasn't updating my UI quite the way you are.
If you still have the need to do both, here is, roughly, how you can flow your app...
Create an
InitailizeBackgroundWorker() method
along with the InitializeTimer so you have
it already initalized before the
Timer fires.
Then set the Timer.Tick
to call the
BackgroundWorker.RunWorkerAsync()
Then you can do all the UI updates from within the RunWorkerAsync by
using the
BackgroundWorker.ReportProgress().