How to implement Timer Elapsed Event on UserInterface thread in Xamarin Android - c#

Am implementing a System Timer on Xamarin Android and i have a problem with the elapsed event not raising a dialog box with the message "Time is up" when the countdown is Over...
I figured the problem might be not implementing the event on the User Interface thread so i need your help accomplishing that...
Here is my code for the Timer
class SecondActivity : AppCompatActivity
{
int counter = 10;
private System.Timers.Timer _timer;
private int _countSeconds;
protected override void OnCreate(Bundle savedInstanceState)
{
_timer = new System.Timers.Timer();
//Trigger event every second
_timer.Interval = 1000;
_timer.Elapsed += OnTimedEvent;
//count down 5 seconds
_timer.Enabled = true;
_countSeconds = 5;
}
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
{
_countSeconds--;
if (_countSeconds == 0) {
_timer.Stop();
Switch switch1 = this.FindViewById<Switch>(Resource.Id.switch2);
Android.App.AlertDialog.Builder dialog = new Android.App.AlertDialog.Builder(this);
Android.App.AlertDialog alert = dialog.Create();
alert.SetTitle("");
alert.SetMessage("Simple Alert");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
});
switch1.Checked = false;
}
I just want the Elapsed event handler to display an alert dialog box when the variable count down equals zero, Thank You

After the first comment pointed me to a related question i found the method to implement the user thread and it now works as intended to display the alert dialog...
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
{
//This is how to make the Timer callback on the UI
RunOnUiThread(() =>
{
_countSeconds--;
if (_countSeconds == 0)
{
Switch switch1 = this.FindViewById<Switch>(Resource.Id.switch2);
Android.App.AlertDialog.Builder dialog = new Android.App.AlertDialog.Builder(this);
Android.App.AlertDialog alert = dialog.Create();
alert.SetTitle("Its over");
alert.SetMessage("Simple Alert");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
});
alert.Show();
switch1.Checked = true;
}
});
}

Related

How can I re-propagate an event after I initially set the handled property to true in an async handler?

I am trying to create a touch and hold event handler with a variable delay in a WPF application by calling a bool task which runs a timer. If the timer elapses, the task returns true. If another event such as touch leave or touch up occurs, the task immediately returns false. Below is my event handler code:
private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
// Set handled to true to avoid clicks
e.Handled = true;
var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
TouchHoldCmd?.Execute(someParam);
else
{
// Here is where I would like to re initiate bubbling up of the event.
// This doesn't work:
e.Handled = false;
}
}
The reason I want it to propagate the event is because, for example, if the user wants to pan the scrollviewer that the element is part of and the panning gesture is started by touching my element, my touchhold works as intended in that the touch and hold command won't get triggered but neither will the scrollviewer start panning.
I tried raising the event manually but this also doesn't seem to work:
bool firedBySelf;
private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
if(firedBySelf)
{
firedBySelf = false;
return;
}
...
else
{
firedBySelf = true;
e.Handled = false;
((FrameworkElement)sender).RaiseEvent(e);
}
}
How can I achieve my goal?
Edit: Here is the class containing the task:
public static class TouchHoldHelper
{
private static DispatcherTimer _timer;
private static TaskCompletionSource<bool> _task;
private static FrameworkElement _element;
private static void MouseUpCancel(object sender, MouseButtonEventArgs e) => CancelHold();
private static void MouseLeaveCancel(object sender, System.Windows.Input.MouseEventArgs e) => CancelHold();
private static void TouchCancel(object sender, TouchEventArgs e) => CancelHold();
private static void AddCancellingHandlers()
{
if (_element == null) return;
_element.PreviewMouseUp += MouseUpCancel;
_element.MouseUp += MouseUpCancel;
_element.MouseLeave += MouseLeaveCancel;
_element.PreviewTouchUp += TouchCancel;
_element.TouchUp += TouchCancel;
_element.TouchLeave += TouchCancel;
}
private static void RemoveCancellingHandlers()
{
if (_element == null) return;
_element.PreviewMouseUp -= MouseUpCancel;
_element.MouseUp -= MouseUpCancel;
_element.MouseLeave -= MouseLeaveCancel;
_element.PreviewTouchUp -= TouchCancel;
_element.TouchUp -= TouchCancel;
_element.TouchLeave -= TouchCancel;
}
private static void CancelHold()
{
if (_timer != null)
{
_timer.Stop();
_timer.Tick -= _timer_Tick;
_timer = null;
}
if (_task?.Task.Status != TaskStatus.RanToCompletion)
_task?.TrySetResult(false);
RemoveCancellingHandlers();
}
private static void _timer_Tick(object sender, EventArgs e)
{
var timer = sender as DispatcherTimer;
timer.Stop();
timer.Tick -= _timer_Tick;
timer = null;
_task.TrySetResult(true);
RemoveCancellingHandlers();
}
public static Task<bool> TouchHold(this FrameworkElement element, TimeSpan duration)
{
_element = element;
_timer = new DispatcherTimer();
_timer.Interval = duration;
_timer.Tick += _timer_Tick;
_task = new TaskCompletionSource<bool>();
AddCancellingHandlers();
_timer.Start();
return _task.Task;
}
}
Edit: to better explain my intended behavior, consider how icons on a smartphone's screen work. If I tap the icon, it starts the app the icon represents. If I touch and move on an icon, it pans the screen. If I touch and hold the icon, it allows me to move the icon so I can place it somewhere else without panning the screen. If I touch and hold the icon but I don't hold it long enough to trigger the moving of the icon, it acts as if I tapped it, starting the app. I am trying to replicate these last 2 behaviors.
I am not saying my current implementation is the right approach but it's what I was able to come up with. If there is any alternative approach, I would be glad to explore it.
Your workflow of setting e.Handled to true and then wanting to set it back to false again strikes me as odd.
From When to Mark Events as Handled
Another way to consider the "handled" issue is that you should generally mark a routed event handled if your code responded to the routed event in a significant and relatively complete way.
Seems like either you'd be using the wrong event or it's as if the folks at Microsoft had gotten it wrong ;)
// Set handled to true to avoid clicks
Nope, they even thought of that, ref Remarks.
You can set Stylus.IsPressAndHoldEnabled="False" to disable the 'click behavior'. Allowing you to fall back to the default WPF pattern of handling the event or letting it tunnel (in this case) forward.
private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
{
TouchHoldCmd?.Execute(someParam);
e.Handled = true;
}
}
However, as you so aptly point out in the comments:
The issue is that the event handler (Element_PreviewTouchDown) is finished executing before the task is. By the time the task is finished, it doesn't make any difference if I change the e.Handled value.
Given the ship has already sailed and you don't want to interfere with the normal functioning of UI elements, we can remove the line that marks the event as handled all together.
private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
{
TouchHoldCmd?.Execute(someParam);
}
}
I don't have a touch-enabled device, so I experimented with the MouseDown/MouseUp events. I attempted to implement a ClickAndHold event, without interfering with the DoubleClick event. What worked for me was to clone the event args of both MouseDown and MouseUp events, and raise them again using the RaiseEvent method. I also had to check explicitly the e.ClickCount property on MouseDown, and allow the event to propagate unhandled in case of e.ClickCount > 1. I have no idea if this approach will work for implementing an interference-free TouchAndHold event. In any case, here is my code:
public static IDisposable OnClickAndHold(Control control, int delay,
MouseButtonEventHandler handler)
{
bool handleMouseDown = true;
bool handleOtherEvents = false;
RoutedEventArgs eventArgsToRepeat = null;
CancellationTokenSource cts = null;
control.MouseDown += Control_MouseDown;
control.MouseUp += Control_MouseUp;
return new Disposer(() =>
{
control.MouseDown -= Control_MouseDown;
control.MouseUp -= Control_MouseUp;
});
async void Control_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!handleMouseDown || e.ClickCount > 1) return;
e.Handled = true;
var clonedArgs = CloneArgs(e);
try
{
cts = new CancellationTokenSource();
handleOtherEvents = true;
await Task.Delay(delay, cts.Token);
handleOtherEvents = false;
}
catch (TaskCanceledException)
{
handleOtherEvents = false;
try
{
handleMouseDown = false;
control.RaiseEvent(clonedArgs);
}
finally
{
handleMouseDown = true;
}
control.RaiseEvent(eventArgsToRepeat);
return;
}
handler(sender, clonedArgs);
}
void Control_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!handleOtherEvents) return;
e.Handled = true;
eventArgsToRepeat = CloneArgs(e);
cts?.Cancel();
}
MouseButtonEventArgs CloneArgs(MouseButtonEventArgs e)
{
return new MouseButtonEventArgs(e.MouseDevice, e.Timestamp,
e.ChangedButton)
{
RoutedEvent = e.RoutedEvent,
Source = control,
};
}
}
private struct Disposer : IDisposable
{
private readonly Action _action;
public Disposer(Action action) => _action = action;
public void Dispose() => _action();
}
Usage example:
OnClickAndHold(Label1, 1000, (s, e) =>
{
MessageBox.Show("ClickAndHold");
});

Stopping focus changed event being handled multiple times in one second

I have a focus changed event handler (uia) on a background MTA thread for updating a list of elements for the current active window which gets fired multiple times in one second when changing active window. I am trying to make it to go once and then wait for one second before handling any other events. Problem at the minute is that the timer is started but never triggered? I guess there is a better way of doing this? A code example would be great.
public void HandleFocusChangedEvent(IUIAutomationElement sender)
{
// A focus changed event has been sent by the the active window or some descendant of it.
// Check that this event hasn't arrived around the time we're removing the event handler on shutdown.
if (!_fAddedEventHandler)
{
return;
}
// All the event handler needs to do is notify the main UI thread that the
// list of elements should be refreshed to make sure it's showing the most current list.
// We only want to do this once every second So use a timer/counter
if (focusChangedCounter == 0)
{
controllerDispatcher.BeginInvoke(_focusChangedEventHandlerDelegate);
focusChangedCounter = 1;
if (focusChangedBufferTimer == null)
{
focusChangedBufferTimer = new System.Windows.Forms.Timer();
focusChangedBufferTimer.Tick += new EventHandler(focusChangedBufferTimer_Tick);
focusChangedBufferTimer.Interval = 1000;
focusChangedBufferTimer.Start();
}
}
}
private void focusChangedBufferTimer_Tick ( object sender, EventArgs e)
{
focusChangedCounter = 0;
focusChangedBufferTimer.Stop();
focusChangedBufferTimer = null;
}
Try this: With each event trigger, reset the timer.
public partial class Form1 : Form
{
Timer t = new Timer();
public Form1()
{
InitializeComponent();
t.Interval = 1000;
t.Tick += ((ss, ee) => {
t.Enabled = false;
focusChangedCounter = 0;
focusChangedBufferTimer.Stop();
focusChangedBufferTimer = null;
});
}
private void HandleFocusChangedEvent(object sender, EventArgs e)
{
t.Enabled = false;
t.Enabled = true;
}
}

Delay request database when keyup (autocomplete)

I have a textbox, I have using both Timer and DispatcherTimer for delay on keyup, but it not actually as my expected.
Event stil fire when Interval is finish. After 5 seconds, the Fiter event stil fire.
Here are my code:
Init variable
DispatcherTimer timerFilter;
//or
//Timer timerFilter = new Timer(5000);
Init event
InitializeComponent();
timerFiter = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timerFiter.Tick += (s, args) =>
{
Filter();
};
//or
//timerFilter.Elapsed += Filter;
When key up
Console.WriteLine("Start");
timerFilter.Start();
//or
//timerFilter.Enabled = true;
When key down
Console.WriteLine("Destroy");
timerFiter.Stop();
//or
//timerFilter.Enabled = false;
My event
private void Filter(Object source, ElapsedEventArgs e)
{
Console.WriteLine("Filter");
timerFiter.Stop();
//or
//timerFilter.Enabled = false;
}
Thanks!
There is always a key up event, so the last thing that will happen after the last key press is:
Console.WriteLine("Start");
timerFilter.Start();
To make sure that your timer stops after the results are filtered, add a Stop call to your Filter handler:
private void Filter(Object source, ElapsedEventArgs e)
{
Console.WriteLine("Filter");
timerFilter.Stop();
}

C# timer I can speed up but not slow down

I have a timer event setup and I would like to change how often the timer event happens by reading a number from a text box. If the box is '10' and you click the update button the event would trigger every 10ms then if you changed to '100' and clicked it would happen every 100ms and so on.
When I run the program however, i can speed up the event frequency (e.g. 100ms to 10ms) but I cannot slow it down (e.g. 10ms to 100ms). Here is the piece of my code that changes the timer when I click:
private void TimerButton_Click(object sender, EventArgs e)
{
getTime = ImgTimeInterval.Text;
bool isNumeric = int.TryParse(ImgTimeInterval.Text, out timerMS); //if number place number in timerMS
label2.Text = isNumeric.ToString();
if (isNumeric)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Enabled = false;
timer.Interval = timerMS;
timer.Elapsed += new ElapsedEventHandler(timerEvent);
timer.AutoReset = true;
timer.Enabled = true;
}
}
public void timerEvent(object source, System.Timers.ElapsedEventArgs e)
{
label1.Text = counter.ToString();
counter = (counter + 1) % 100;
}
If anyone knows what I may be doing wrong it would be greatly appreciated.
The problem with this code is, that you create a new Timer each time you click the button. Try to create the timer outside the method. You think it's only goes faster, but instead multiple timers trigger the timerEvent
private System.Timers.Timer _timer;
private void CreateTimer()
{
_timer = new System.Timers.Timer();
_timer.Enabled = false;
_timer.Interval = 100; // default
_timer.Elapsed += new ElapsedEventHandler(timerEvent);
_timer.AutoReset = true;
_timer.Enabled = true;
}
private void TimerButton_Click(object sender, EventArgs e)
{
bool isNumeric = int.TryParse(ImgTimeInterval.Text, out timerMS); //if number place number in timerMS
label2.Text = isNumeric.ToString();
if (isNumeric)
{
_timer.Interval = timerMS;
}
}
public void timerEvent(object source, System.Timers.ElapsedEventArgs e)
{
label1.Text = counter.ToString();
counter = (counter + 1) % 100;
}
Make sure that the CreateTimer is called in the constructor/formload. Also you can now stop the timer within another button event. With _timer.Enabled = false;
You're always creating a new timer and never stopping the old timer. When you "change" it from 100 to 10 your 100ms timer is still firing every 100 ms, so every 100ms two timers are firing at around the same time.
You need to "remember" the old timer so that you can stop it. Or, better yet, just have only one timer that you change the interval on.
private System.Timers.Timer timer = new System.Timers.Timer();
public Form1()
{
timer.Enabled = false;
timer.AutoReset = true;
timer.Elapsed += timerEvent;
}
private void TimerButton_Click(object sender, EventArgs e)
{
getTime = ImgTimeInterval.Text;
bool isNumeric = int.TryParse(ImgTimeInterval.Text, out timerMS); //if number place number in timerMS
label2.Text = isNumeric.ToString();
if (isNumeric)
{
timer.Interval = timerMS;
timer.Enabled = true;
}
}
Well the basic problem is that you're building a new one every time. Make a private timer:
private System.Timers.Timer _timer = new System.Timers.Timer();
and then fix it up when the button is clicked:
if (isNumeric)
{
_timer.Stop();
_timer.Interval = timerMS;
_timer.Start();
}
and then in the .ctor, do this:
_timer.Elapsed += new ElapsedEventHandler(timerEvent);
Now you have a single timer that you are just modifying as the user changes the value in the text box.

moving a button using a timer

I have four buttons that are called "ship1,ship2" etc.
I want them to move to the right side of the form (at the same speed and starting at the same time), and every time I click in one "ship", all the ships should stop.
I know that I need to use a timer (I have the code written that uses threading, but it gives me troubles when stopping the ships.) I don't know how to use timers.
I tried to read the timer info in MDSN but I didn't understand it.
So u can help me?
HERES the code using threading.
I don't want to use it. I need to use a TIMER! (I posted it here because it doesnt give me to post without any code
private bool flag = false;
Thread thr;
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;
thr = new Thread(Go);
thr.Start();
}
private delegate void moveBd(Button btn);
void moveButton(Button btn)
{
int x = btn.Location.X;
int y = btn.Location.Y;
btn.Location = new Point(x + 1, y);
}
private void Go()
{
while (((ship1.Location.X + ship1.Size.Width) < this.Size.Width)&&(flag==false))
{
Invoke(new moveBd(moveButton), ship1);
Thread.Sleep(10);
}
MessageBox.Show("U LOOSE");
}
private void button1_Click(object sender, EventArgs e)
{
flag = true;
}
Have you googled Windows.Forms.Timer?
You can start a timer via:
Timer timer = new Timer();
timer.Interval = 1000; //one second
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
timer.Start();
You'll need an event handler to handle the Elapsed event which is where you'll put the code to handle moving the 'Button':
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
MoveButton();
}

Categories

Resources