Using UserControl paint event to trigger idle timer? - c#

I've been tasked with triggering a logout event after a period of inactivity. I have created some code that appears to be working in my application, but I'm assuming I took an abnormal approach as I haven't seen similar solutions proposed to others attempting to do the same. My approach relies on the UserControl.Paint event, is there a better practice? Are there limitations to the Paint event I may be unaware of?
public partial class MyControl : UserControl
{
private Timer _idleTimer;
private SubControl _sc;
public MyControl()
{
InitializeComponent();
// Create the auto logout timer
_idleTimer = new Timer();
_idleTimer.Interval = 300000;
_idleTimer.Tick += btnLogout_Click;
_idleTimer.Enabled = true;
// Create the subcontrol.
_sc = new SubControl();
_sc.Paint += (o, i) => _idleTimer.Reset(); // Extension method that call Stop and Start
this.tabPage1.Controls.Add(_sc);
}
}
Thanks - Derrick

Try using the Application.Idle event instead:
Application.Idle += (o, i) => { _idleTimer.Reset(); };
The paint event isn't always firing, so it's unreliable as your source of inactivity.

Related

Is it possible to get Timer.Tick event after diposing it

I have a question about System.Windows.Forms.Timer. Is it possible to get Tick event after disposing it? For example, if the message is in the message loop and I dispose the timer meanwhile. If it is possible what is the best way to prevent against it. Do you now any good sources explaining it, because I couldn't find anything explaining it. Here is same code explaining my problem:
namespace TestTimer
{
public partial class Form1 : Form
{
ObjectWithTimer obj = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(obj != null)
{
obj.Deinit();
obj = null;
}
obj = new ObjectWithTimer();
}
}
public class ObjectWithTimer
{
public Object o = new object();
public Timer timer = new Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 10;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
if (disposed)
{
//Is it possible to get here
if (timer.Enabled) return;
}
//doing something
}
}
}
Understanding how timers work can help you feel better about it. They are implemented by the operating system, the underlying winapi call to start a timer is SetTimer(). The OS then posts a notification whenever the timer ticks, you get a WM_TIMER message. The plumbing in Winforms ensures that your Tick event handler runs when this message is received.
These messages are stored in the message queue, an internal data structure associated with a window. This queue serializes messages, it is the basic mechanism that ensures that you for example can never lose a mouse click or a keyboard key press, even when the window is unresponsive because the UI thread is busy with something else.
This queue gives reason to be cautious, what happens when the queue stores a WM_TIMER message when you disposed the timer? Unless something drastic is done, you'd still get that message and your Tick event handler will fire.
But no need to worry, WM_TIMER belongs to a small group of messages that are generated in a special way. They are synthesized messages, it is only ever generated when your program asks for a message with GetMessage(). Other common messages that belong that group are WM_PAINT, it fires the Paint event. Note how you can call Invalidate() as often as you like, you still get only a single Paint event. WM_MOUSEMOVE is another one, it fires the MouseMove event. Something you can reason about, no matter how fast you move the mouse, you can never flood the message queue with mouse-move messages.
Another characteristic of these synthesized messages is that they appear to have a "low priority". Given is that they are only ever synthesized when the message queue is empty. Which is why keyboard messages and mouse clicks always generate an event ahead of a paint.
Long story short, you can only get a WM_TIMER message if you ask for a message and the timer is still alive. The Timer.Dispose() method calls KillTimer() under the hood. Which ends any opportunity to still get a Tick event. Only possible way that could get screwed up is when you call the Stop() or Dispose() methods from a worker thread. Don't do that.
The Windows Forms Timer is single threaded so is not possible that while disposing it you are in timer_Tick.
Also you are not detaching your event in deinit function.
This is very easy to test. I've modified your code a bit:
public class Form1 : Form
{
public Form1()
{
var button = new Button();
button.Click += button1_Click;
Controls.Add(button);
}
private void button1_Click(object sender, EventArgs e)
{
var obj = new ObjectWithTimer();
Thread.Sleep(2000);
obj.Deinit();
}
}
public class ObjectWithTimer
{
public System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
"Ticked".Dump();
}
}
The Thread.Sleep ensures the UI thread is occupied while the timer does its ticking.
The result? No, the Tick will not fire after the timer is disabled. Even the timer.Tick -= new EventHandler(timer_Tick); is unnecessary.

Throttle an Event Handler

In my WPF application, I have an event handler that gets called on the MouseEnter event of my UI element:
myUiElement.MouseEnter += myEventHandler
I would like to throttle myEventHandler so it doesn't get called more than once every second. How can I do this? Is Rx the best approach just for this? I'm using .NET 4.0 if it makes a difference.
Also, I need to make sure that the MouseLeave event always gets called before the next MouseEnter event; do I need to manage this on my own? Or is the framework already designed so that MouseLeave events will always be called before the next MouseEnter event? What if I have asynchronous code in these event handlers?
Using Rx, you want to use the Sample method or Throttle.
Something like this should work (untested):
Observable
.FromEventPattern<TextChangedEventArgs>(myUiElement, "MouseEnter")
.Sample(TimeSpan.FromSeconds(1))
.Subscribe(x => ... Do Stuff Here ...);
The difference between Sample and Throttle is that Sample will take a value every 1 second no matter when the last value was taken, whereas Throttle will take a value and then wait another 1 second before taking another.
It probably depends on what you are shooting for...
You could use reactive extensions, but you could accomplish this just as easily with a timer.
Set a flag along with a Timer. When the timer tick event fires, set the flag to false, disable the timer, and run the code for your event. Then, in your control event handlers, have the handler code skipped if the flag is set.
bool flag;
DispatcherTimer timer;
public constructor()
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s,e) => {
flag = false;
timer.Stop()
DoThrottledEvent();
}
}
void mouse_enter(object sender, MouseEventArgs args)
{
if(!flag)
{
flag = true;
timer.Start();
}
}
void DoThrottledEvent()
{
//code for event here
}
Reactive extensions introduces an extra dependency, but they are a bit of fun. If you are interested, go for it!
Another approach would be to use a private field to keep track of the "time" when the last mouse event occurred, and only continue processing if that time was more than one second ago.
DateTime _lastMouseEventTime = DateTime.UtcNow;
void OnMouseEnter(object sender, MouseEventArgs e)
{
DateTime now = DateTime.UtcNow;
if (now.Subtract(_lastMouseEventTime).TotalSeconds >= 1)
{
// do stuff...
}
_lastMouseEventTime = now;
}
This ensures that "stuff" gets done at least one second apart, which is what I think you were asking for.

WinRT timed logout

I am developing a WinRT app. One of the requirements is that the app should have a "timed logout" feature.
What this means is that on any screen, if the app has been idle for 10 mins, the app should logout and navigate back to the home screen.
The brute force way of doing this obviously would be to hook up pointer pressed events on every grid of every page and resetting the timer if any of these events is fired but I was wondering if there was a more elegant and more reliable way of doing this.
Thanks,
Rajeev
With the use of DispatcherTimer & several events you can achieve that.
DispatcherTimer Timer;
private void InitializeTimer()
{
Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
Window.Current.CoreWindow.PointerMoved += CoreWindow_PointerMoved;
Window.Current.CoreWindow.PointerPressed += CoreWindow_PointerPressed;
Timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromMinutes(10);
Timer.Tick += Timer_Tick;
Timer.Start();
}
private void CoreWindow_PointerPressed(CoreWindow sender, PointerEventArgs args)
{
Timer.Start();
}
private void CoreWindow_PointerMoved(CoreWindow sender, PointerEventArgs args)
{
Timer.Start();
}
private void Dispatcher_AcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
{
Timer.Start();
}
private void Timer_Tick(object sender, object e)
{
Timer.Stop();
//TODO: Do logout.
}
I'm not aware of anything built in, but rather than attaching to Grids and etc., I'd suggest you attach event handlers to the current CoreWindow (documentation) for the various types of events that you would need to track to determine idleness.
If you did attach to a Grid for example, you'd find controls that use Popup won't trigger the events. A ComboBox for example wouldn't be tracked by the event handlers.
For example, you might do this:
var core = CoreWindow.GetForCurrentThread();
core.KeyDown += (sender, kdArgs) => {
// reset timeout
};
core.PointerMoved = core.PointerMoved = (sender, pmArgs) {
// reset timeout (maybe with a bit of logic to prevent tiny mouse drift from
// causing false positives
};
// etc. (whatever else makes sense)
The code relies on the GetForCurrentThread call (documentation) which returns the instance of the CoreWindow that is the host for all content.

Event triggering after subscription

I have a class that contains a queue and a form that contains a listbox.
When the user opens the form it gets all of the accumulated objects from the queue and shows them in the listbox. While the form is open as soon as the queue gets a new item it notifies the form of the new item via a custom event.
After closing the form the data will accumulate again.
My problem is the following: As soon the form is subscribed to the notification event it should dump all of the queue to the form and keep dumping it as long as someone is subscribed to the event. It should not wait until another item is added to the queue.
One solution would be to use a timer to check if there are any subscriptions to the event and then dump it. It is not much but i would be wasting resources with the timer.
It would seem to be better if the form's subscription to the event could itself trigger the event. The app is very modular and modules communicate through events to a eventNexus and then the nexus notifies everyone who needs to know.
Since an event is an object as well, it should be possible to accomplish something like this but I did not manage to find any links.
You can customize the adding and removing of event handlers in the code of the event itself by using the add and remove statements:
private EventHandler onMyEvent;
public event MyEventHandler MyEvent
{
add
{
// run when event handler is added ( += )
onMyEvent = (MyEventHandler)Delegate.Combine(onMyEvent, value);
// Add additional, custom logic here...
}
remove
{
// run when event handler is removed ( -= )
onMyEvent = (MyEventHandler)Delegate.Remove(onMyEvent, value);
}
}
Here you can add your own code to trigger actions upon adding or removing your event handler.
For timer your can try with this code
private static System.Timers.Timer aTimer;
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 2 seconds (2000 milliseconds).
aTimer.Interval = 2000;
aTimer.Enabled = true;
Your function
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//Your treatment
}

Call method on active page in Silverlight

I am trying to build a clock application in Silverlight.
My application has multiple pages, one for analog, one for digital and one for a timer.
If I want to have a separate class that handles the time-keeping as follows:
private void startTimer()
{
DispatcherTimer myDispatcherTimer = new DispatcherTimer();
myDispatcherTimer.Interval = new TimeSpan(0,0,1);
myDispatcherTimer.Tick += new EventHandler(eachClick);
myDispatcherTimer.Start();
}
How can I get the current page that is displayed so that the method can use that page's event handler?
What is the best way to go about this?
I dont really understand what exactly you're trying to do. "How can I get the current page so that the method can use that page event handler?"
If you have three pages, and you're trying to synchronize the time between all three of them, why dont you create a static Timer class.
I dont know, maybe something like this:
public static class StaticTimer
{
public delegate void Time(DateTime time);
public static event Time Tick = delegate { };
static StaticTimer()
{
DispatcherTimer myDispatcherTimer = new DispatcherTimer();
myDispatcherTimer.Interval = new TimeSpan(0,0,1);
myDispatcherTimer.Tick += (s,e) => Tick(DateTime.Now);
myDispatcherTimer.Start();
}
}
Then, from each page, just subscribe to the Tick event when you create it.
void AnalogPage_OnLoaded( ... )
{
StaticTimer.Tick += someEventHandler;
}
void someEventHandler(DateTime time)
{
if(thisIsCurrentPage)
{
clock.Update(time);
}
}

Categories