I've an "animateMyWindow" class to change opened window's opacity with Timer.
namespace POCentury
{
class animateMyWindow
{
Timer _timer1 = new Timer();
Window _openedWindow = null;
public void animationTimerStart(object openedWindow)
{
if (openedWindow == null)
{
throw new Exception("Hata");
}
else
{
_openedWindow = (Window)openedWindow;
_timer1.Interval = 1 * 25;
_timer1.Elapsed += new ElapsedEventHandler(animationStart);
_timer1.AutoReset = true;
_timer1.Enabled = true;
_timer1.Start();
}
}
private void animationStart(object sender, ElapsedEventArgs e)
{
if (_openedWindow.Opacity == 1)
animationStop();
else
_openedWindow.Opacity += .1;
}
private void animationStop()
{
_timer1.Stop();
}
}
}
animationStart function can't reach my window because it is working on a different thread.
I've tried Dispatcher.BeginInvoke and can't make it work.
Can you help me with doing that?
Basically, you can't access the openedWindow inside the animationStart event because it's happening in a different thread. You need the Dispatcher to do that.
Dispatcher.BeginInvoke(new Action(() =>
{
if (_openedWindow.Opacity == 1)
animationStop();
else
_openedWindow.Opacity += .1;
}));
Related
I have application in WPF with C#.net in which we have lot of expensive operations. Recently we got this requirement: For long running processes display the busy cursor till 3 seconds, if operation is exceeding 3 second display the splash screen until operation is finished. I have searched a lot over the net but nothing seems to be relevant. Any support will be high appreciated.
We have tried something, it works but in few case it is not giving expected results. Any help would be highly appreciated.
When my internal logic display any alert message and wait for the user input, Busy Cursor function timer keep running, we got the splash too.
public class BusyCursor:IDisposable
{
private Cursor _previousCursor;
System.Timers.Timer _timer
public BusyCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
_timer = new System.Timers.Timer();
_timer.Interval = 3000;
_timer.Elapsed += timer_Tick;
_timer.Start();
}
public void timer_Tick(object sender, ElapsedEventArgs e)
{
Application.Current.Dispatcher.Invoke((new Action(() =>
{
if (!DXSplashScreen.IsActive)
{
DXSplashScreen.Show<TrippsSplashScreen>();
}
Mouse.OverrideCursor = Cursors.Arrow;
_timer.Stop();
})), DispatcherPriority.ApplicationIdle);
}
#region IDisposable Members
public void Dispose()
{
_timer.Stop();
if (DXSplashScreen.IsActive)
{
DXSplashScreen.Close();
}
Mouse.OverrideCursor = Cursors.Arrow;
}
#endregion
}
Usage:
using (new BusyCursor())
{
//logic ---
}
Thanks
bool calculating = false;
bool showingSplash = false;
void Meth(Task[] expensiveCalls)
{
Task.Factory.StartNew(() =>
{
calculating = true;
Task.Factory.StartNew(() =>
{
Task.Delay(3000).Wait();
if (calculating)
{
showingSplash = true;
//Application.Current.Dispatcher.Invoke(() => show_sphlash());
}
});
Task.WaitAll(expensiveCalls);
calculating = false;
if (showingSplash)
{
//Application.Current.Dispatcher.Invoke(() => hide_sphlash());
showingSplash = false;
}
}
);
}
What I'm trying to do is start a function, then change a bool to false, wait a second and turn it to true again. However I'd like to do it without the function having to wait, how do I do this?
I can only use Visual C# 2010 Express.
This is the problematic code. I am trying receive user input (right arrow for example) and move accordingly, but not allow further input while the character is moving.
x = Test.Location.X;
y = Test.Location.Y;
if (direction == "right")
{
for (int i = 0; i < 32; i++)
{
x++;
Test.Location = new Point(x, y);
Thread.Sleep(31);
}
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
int xmax = Screen.PrimaryScreen.Bounds.Width - 32;
int ymax = Screen.PrimaryScreen.Bounds.Height - 32;
if (e.KeyCode == Keys.Right && x < xmax) direction = "right";
else if (e.KeyCode == Keys.Left && x > 0) direction = "left";
else if (e.KeyCode == Keys.Up && y > 0) direction = "up";
else if (e.KeyCode == Keys.Down && y < ymax) direction = "down";
if (moveAllowed)
{
moveAllowed = false;
Movement();
}
moveAllowed = true;
}
Use Task.Delay:
Task.Delay(1000).ContinueWith((t) => Console.WriteLine("I'm done"));
or
await Task.Delay(1000);
Console.WriteLine("I'm done");
For the older frameworks you can use the following:
var timer = new System.Timers.Timer(1000);
timer.Elapsed += delegate { Console.WriteLine("I'm done"); };
timer.AutoReset = false;
timer.Start();
Example according to the description in the question:
class SimpleClass
{
public bool Flag { get; set; }
public void function()
{
Flag = false;
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (src, args) => { Flag = true; Console.WriteLine("I'm done"); };
timer.AutoReset = false;
timer.Start();
}
}
I realize an answer has already been accepted, and I do like ixSci's answer where he recommends the use of a Timer object to accomplish OP's goal.
However, using System.Timers.Timer specifically introduces threading considerations. And to ensure correctness in this case, more code is required to properly synchronize the boolean flag value. Basically, anywhere where the flag is read or written, the code region would need to have a lock statement defined around it.
It would have to look something like this:
private final object flagLock = new object();
private bool moveAllowed = true;
private System.Timers.Timer timer = new System.Timers.Timer();
public Form1()
{
this.timer.Interval = 1000;
this.timer.AutoReset = false;
this.timer.Elapsed += (s, e) =>
{
// this DOES NOT run on the UI thread, so locking IS necessary to ensure correct behavior.
this.timer.Stop();
lock (this.flagLock) {
this.moveAllowed = true;
}
};
}
// The code in this event handler runs on the UI thread.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
// Locking is necessary here too.
lock (this.flagLock) {
if (this.moveAllowed)
{
this.moveAllowed = false;
Movement();
this.timer.Start(); // wait 1 second to reset this.moveAllowed to true.
}
}
}
Alternatively, to avoid having to think about threads, perhaps OP could consider using a different flavor of the Timer class. Namely: System.Windows.Forms.Timer. This way, the boolean flag will always be read/written on the UI thread, and no extra locking of any sort is required to ensure correctness.
In this case, the code would look something like this:
private bool moveAllowed = true;
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
this.timer.Interval = 1000;
this.timer.Tick += (s, e) =>
{
// this runs on the UI thread, so no locking necessary.
this.timer.Stop(); // this call is necessary, because unlike System.Timers.Timer, there is no AutoReset property to do it automatically.
this.moveAllowed = true;
};
}
// The code in this event handler runs on the UI thread.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (this.moveAllowed)
{
this.moveAllowed = false;
Movement();
this.timer.Start(); // wait 1 second to reset this.moveAllowed to true.
}
}
I am working on iOS Application in Xamarin.
timer1 = new System.Timers.Timer();
timer1.Interval = 1000;
//Play.TouchUpInside += (sender,e)=>
//{
timer1.Enabled = true;
Console.WriteLine("timer started");
timer1.Elapsed += new ElapsedEventHandler(OnTimeEvent);
//}
This is what i have written in viewdidload();
public void OnTimeEvent(object source, ElapsedEventArgs e)
{
count++;
Console.WriteLine("timer tick");
if (count == 30)
{
timer1.Enabled = false;
Console.WriteLine("timer finished");
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
StartTimer.Text = Convert.ToString(e.SignalTime.TimeOfDay); // this works!
});
})).Start();
}
else
{
//adjust the UI
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
StartTimer.Text = Convert.ToString(e.SignalTime.TimeOfDay); // this works!
});
})).Start();
timer1.Enabled = false;
Console.WriteLine("timer stopped");
}
}
This is the event I have called when I clicked on button play. I want this method to keep running so that time get updated on the label (starttimer.Text) in the UI. Like Runnable Interface we use in Android, what do we have to use in iOS to keep it running?
Use async - much cleaner (no marshalling to get you back on the main thread again!)
private int _duration = 0;
public async void StartTimer() {
_duration = 0;
// tick every second while game is in progress
while (_GameInProgress) {
await Task.Delay (1000);
_duration++;
string s = TimeSpan.FromSeconds(_duration).ToString(#"mm\:ss");
btnTime.SetTitle (s, UIControlState.Normal);
}
}
//before loading the view
public override void ViewWillAppear(bool animated)
{
...
StartTimer();
}
// when view is loaded
public override void ViewDidLoad()
{
base.ViewDidLoad();
....
UpdateDateTime();
}
private void UpdateDateTime()
{
var dateTime = DateTime.Now;
StartTimer.Text = dateTime.ToString("HH:mm:ss");
}
private void StartTimer()
{
var timer = new Timer(1000);
timer.Elapsed += (s, a) => InvokeOnMainThread(UpdateDateTime);
timer.Start();
}
For performance i have to replace DispatcherTimer with a BackGroundWorker to handle a intensive query that runs every 5 sec by using a Threading Timer.
I no longer get any result when implementing the following code, most of the times my application shuts down as well.
public void CaculateTimeBetweenWegingen()
{
if (listWegingen.Count > 1)
msStilstand = (DateTime.Now - listWegingen[listWegingen.Count - 1]).TotalSeconds;
if(msStilstand >= minKorteStilstand)
{
stopWatch.Start();
if (msStilstand >= minGroteStilstand)
{
FillDuurStilstandRegistrationBtn();
if (zelfdeStilstand == false)
{
CreateRegistrationButton();
zelfdeStilstand = true;
}
if (msStilstand <= maxGroteStilstand){
//....
}
}
}
else //new weging
{
if (stopWatch.IsRunning == true)
{
timerStilstand.Stop();
stopWatch.Stop();
//huidige registrationBtn
if (GlobalObservableCol.regBtns.Count > 1)
{
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].StopWatchActive = false;
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].DuurStilstand =
String.Format("{0:D2}:{1:D2}:{2:D2}", stopWatch.Elapsed.Hours, stopWatch.Elapsed.Minutes, stopWatch.Elapsed.Seconds);
}
}
zelfdeStilstand = false;
}
}/*CaculateTimeBetweenWegingen*/
public void CreateRegistrationButton()
{
InitializeDispatcherTimerStilstand();
RegistrationButton btn = new RegistrationButton(GlobalObservableCol.regBtns.Count.ToString());
btn.RegistrationCount = GlobalObservableCol.regBtnCount;
btn.Title = "btnRegistration" + GlobalObservableCol.regBtnCount;
btn.BeginStilstand = btn.Time;
GlobalObservableCol.regBtns.Add(btn);
GlobalObservableCol.regBtnCount++;
btn.DuurStilstand = String.Format("{0:D2}:{1:D2}:{2:D2}", 0, 0, 0);
}
public void InitializeDispatcherTimerWeging()
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new Timer(callback);
timerWegingen.Change(0, 5000);
}
public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
worker.RunWorkerAsync();
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
The button gets refilled with new values every 1 sec trough a other timer. "DuurStilstand" is a dependency property
private void FillDuurStilstandRegistrationBtn()
{
TimeSpan tsSec = TimeSpan.FromSeconds(stopWatch.Elapsed.Seconds);
TimeSpan tsMin = TimeSpan.FromMinutes(stopWatch.Elapsed.Minutes);
TimeSpan tsHour = TimeSpan.FromMinutes(stopWatch.Elapsed.Hours);
if (GlobalObservableCol.regBtns.Count >= 1
&& GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].StopWatchActive == true)
{
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].DuurStilstand =
String.Format("{0:D2}:{1:D2}:{2:D2}", tsHour.Hours, tsMin.Minutes, tsSec.Seconds);
}
}
All the above code is written in a separate c# class.
How exactly do i make this code work with BackGroundWorker and how / where to update the GUI with Dispatcher/Invoke. Been trying for a long while and i cannot seem to solve this issue atm.
I have also seen that the Complete method of the BackGroundWorker can be used to update the GUI, but not sure how exactly. The buttons get created and saved in a ObservableCollection.
public static ObservableCollection<RegistrationButton> regBtns = new ObservableCollection<RegistrationButton>();
Some examples would be most useful. Since i know more or less what has to been done but not exactly sure how to implement it.
Best Regards,
Jackz
I don't understand the meaning of your app but you'll be able to update UI like this
public void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.ApplicationIdle, new Action(() =>
{
//do your stuff
}));
}
Maybe the Rendering event should help you to deal with the UIThread.
CompositionTarget.Rendering += (s, args) =>
{
//do your stuff
};
Hope it help
In the following code example, I want to change the color of the Foreground text of a TextBox from my BackgroundThread, but it gets the error:
This thread cannot access this object
since it is in another thread.
What do I have to change in the code below so that the background worker thread can change the foreground color in the TextBox?
Answer:
Thanks Andy, it was just that small oversight, here is the corrected code for posterity's sake:
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.Windows.Media;
namespace TestBackgroundWorker7338
{
public partial class Window1 : Window
{
private BackgroundWorker _worker;
int _percentageFinished = 0;
public Window1()
{
InitializeComponent();
ButtonCancel.IsEnabled = false;
}
private void Button_Start(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (s, args) =>
{
BackgroundWorker worker = s as BackgroundWorker;
int numberOfTasks = 300;
for (int i = 1; i <= numberOfTasks; i++)
{
if (worker.CancellationPending)
{
args.Cancel = true;
return;
}
Thread.Sleep(10);
float percentageDone = (i / (float)numberOfTasks) * 100f;
worker.ReportProgress((int)percentageDone);
}
};
_worker.ProgressChanged += (s,args) =>
{
_percentageFinished = args.ProgressPercentage;
ProgressBar.Value = _percentageFinished;
Message.Text = _percentageFinished + "% finished";
if (_percentageFinished < 500)
{
Message.Text = "stopped at " + _percentageFinished + "%";
}
else
{
Message.Text = "finished";
}
if (_percentageFinished >= 70)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if (_percentageFinished >= 40)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
else if (_percentageFinished >= 10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Brown);
}
else
{
InputBox.Foreground = new SolidColorBrush(Colors.Black);
}
};
_worker.RunWorkerCompleted += (s,args) =>
{
ButtonStart.IsEnabled = true;
ButtonCancel.IsEnabled = false;
ProgressBar.Value = 0;
};
_worker.RunWorkerAsync();
ButtonStart.IsEnabled = false;
ButtonCancel.IsEnabled = true;
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
}
}
}
As Andy said changing properties of controls must happen on the UI thread.
WPF provides a Dispatcher class that makes it easier to route calls for controls to the appropriate UI thread. Controls in WPF exposes a Dispatcher property object to dispatch the call to an appropriate UI thread.
You can use this as follows (I would add this after the line worker.ReportProgress((int)percentageDone); in the Button_Start method):
// the Window class exposes a Dispatcher property, alternatively you could use
// InputBox.Dispatcher to the same effect
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new Action(() => ChangeForegroundColor(percentageDone));
}
else
{
ChangeForegroundColor(percentageDone);
}
...
private void ChangeForegroundColor(int percentageDone)
{
if (percentageDone >= 90)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if(percentageDone >=10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
}
The ProgressChanged delegate is run on the UI thread. If you set the BackgroundColor there instead of in DoWork, that should allow you to set the color without the error.