Long Press Detector with c# in xamarin.forms - c#

I am making a simple calculator app where the DEL button removes the last digit in the viewport, but if held down for one second it deletes everything in the viewport. Right now If I press the button once, it deletes the last digit whether I hold it down or not. If I press it down again (held down or just a regular tap) it clears everything in the viewport and I cannot type anything else in.
Firstly I create the timer like so:
public System.Timers.Timer timer = new System.Timers.Timer(1);
and then I have a function for when the 'DEL' button is pressed:
private void DeletePressed(object sender, EventArgs args)
{
timer.Stop();
timer.Start();
timer.Elapsed += AllClear;
}
a function to stop the timer and delete the last character from the viewport when the button is released:
private void DeleteReleased(object sender, EventArgs args)
{
timer.Stop();
if (CalculatorString.Length > 0)
{
CalculatorString = CalculatorString.Remove(CalculatorString.Length - 1);
}
viewport.Text = CalculatorString;
}
and finally the procedure that is called when the timer finishes:
private void AllClear(Object Source, System.Timers.ElapsedEventArgs e)
{
timer.Stop();
CalculatorString = "";
viewport.Text = CalculatorString;
}
however none of what I expected tohas happened. I appreciate any help in advance :)

There is no direct way to listen long press in Xamarin.Forms(PCL). You have to write separate custom renderers for each platform to Listen long press and communicate it to the PCL.
You can refer this link for code example for doing the same.

Related

Can not click button when a method running

private void AddMyScrollEventHandlers()
{
VScrollBar vScrollBar1 = new VScrollBar();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
break;
}
System.Threading.Thread.Sleep(200);
}
}
private void button2_Click(object sender, EventArgs e)
{
// vScrollBar1.Scroll
}
I am new in C#. I was working on scroll. What I wanted here is, if anyone click button1 then scroll automatically move to the end and I wanted to show gradual value in label1. Also when someone click button2 scrolling stop.
Now the problem is label1 do not show gradual change in value. It shows value once when the scrolling stop.
Also when scrolling continue i,e when while loop is working I can not click on button2. Actually I can not click on the form even.
Someone please give me some idea how to do this.
This happens because the thread that is performing the task is busy, and it's the same thread that updates the UI. You can use a multithreading solution. Take a look at
BackgroundWorker
All the UI events run in the main thread of the application, so the application can only process one event at a time. When the application is processing an event, no other event will be processed.
Since you are doing a UI related work periodically, the best option is to use the Timer class:
Drop Timer from the toolbox into the form.
In the properties window, set the interval to 200.
Double click the timer object to create the Tick event handler.
Put this code in the newly created timer1_Tick method:
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
timer1.Stop();
}
Change your methods as below:
private void AddMyScrollEventHandlers()
{
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
}
Now you're done.
I would recommend using BackgroundWorker control, as suggested by Agustin Meriles. However, one more important thing to note is that You should use Control.Invoke(...) method to update controls from another thread.
I've modified Your code, tested it in a sample application and it seems to work correctly.
First, add a new BackgroundWorker control to Your form and assign backgroundWorker1_DoWork to its DoWork event.
Then, You can use the code below:
private void button1_Click(object sender, EventArgs e)
{
//code from here is moved to BackgroundWorker control
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//while (true)
//the condition directly in the while looks more clear to me
while (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
//update controls using Invoke method and anonymous functions
vScrollBar1.Invoke((MethodInvoker)delegate() { vScrollBar1.Value += 1; });
label1.Invoke((MethodInvoker)delegate() { label1.Text = vScrollBar1.Value.ToString(); });
//when called inside BackgroundWorker, this sleeps the background thread,
//so UI should be responsive now
System.Threading.Thread.Sleep(200);
}
}
If You have any problems when using this code, please let me know.
Update
As mentioned in the comments, You could also use ProgressChanged event of the BackgroundWorker. It requires some more changes in the code, but is more suitable in this case. You can find some information about it here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx.
If You are not going to add any other code with more processing in the while loop, You can also use Timer control, as suggested by MD.Unicorn in his answer.

Windows 8 / XAML Double Tap event always preceded by Tap

I've got a button in an app. I want it to do different things depending on whether I tap it (pause an animation) or double-tap (restart the animation)
however, when I double-tap, it seems to fire the tap event first and then the double-tap in quick succession. Is there a way around this? Is this a known issue or am I making a rookie mistake?
Edit: For those asking, I'm using the Tapped and DoubleTapped events.
Give the some pause to single tab . If there is double tab occur then single tab event eliminated
bool singleTap;
private async void control_Tapped_1(object sender, TappedRoutedEventArgs e)
{
this.singleTap = true;
await Task.Delay(200);
if (this.singleTap)
{
// Single tab Method .
}
}
private void control_DoubleTapped_1(object sender, DoubleTappedRoutedEventArgs e)
{
this.singleTap = false;
// Double tab Method
}
You might want to consider letting it do both. Think about it like double clicking a folder in windows. Users will be used to something happening on the first click (highlighting the folder) and they will be expecting the double click to both highlight, then navigate.
All in all it seems like a design issue, not a technical one.
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
bool _singleTapped = false;
//Tapped event handler
private async void control_Tapped(object sender, TappedRoutedEventArgs e)
{
_singleTapped = true;
//var x = GetDoubleClickTime();
//GetDoubleClickTime() gets the maximum number of milliseconds that can elapse between a first click and a second click
await Task.Delay(TimeSpan.FromMilliseconds(GetDoubleClickTime()));
if (!_singleTapped)
{ return; }
//SingleTapped code
}
//DoubleTapped event handler
private async void control_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
_singleTapped = false;
//DoubleTapped code
}

Monotouch TouchUpInside does not work, TouchDown works

I have seen some topics about this subject in Objective-C. I read a lot of them, spent 2 days on it on trying to find a solution and none of them worked for me. I am mainly coding in C#. Since my problem behaviour (fire only when leaving/re-enter button) and context (C#) is a bit different. So, I will try my chance by asking my question here.
I will try to keep it simple.
Here is a sample of code:
private UIButton _buttonTest;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
_buttonTest = new UIButton(new RectangleF(10, 70, 50, 50));
_buttonTest.SetTitle("0", UIControlState.Normal);
_buttonTest.TouchUpInside += HandleButtonTestTouchUpInside;
_buttonTest.BackgroundColor = UIColor.Red;
this.View.AddSubview(_buttonTest);
}
void HandleButtonTestTouchUpInside (object sender, EventArgs e)
{
string textNumber = _buttonTest.Title(UIControlState.Normal);
// Increment Number
_buttonTest.SetTitle((int.Parse(textNumber)+1).ToString(), UIControlState.Normal);
}
There is a button and the title text is set to "0".
When the user click on it, it increments the number (title) by 1.
This code usually works very well!
However, it does not work in some of my classes for some unknown reasons...
Here is the problem:
TouchUpInside does not fire. ... However, if I hold the button with a finger and keep holding the finger while leaving the button and then re-entering the button then release the button, then.... it will fire the TouchUpInside, .... so the finger need to leave and re-renter the button while holding the button to make the TouchUpInside fires. Usually, this code works very well.
Things Checked:
if I replace TouchUpInside by TouchDown, then TouchDown works.
'User Interaction Enabled' is set to True for all superviews.
All views frames (among the superviews) seem to be inside their superview frames.
I moved the _buttonTest around and I noticed that the _buttonTest TouchUpInside does not fire correctly in the View, the SuperView, the SuperSuperView.... but it fires correctly in the SuperSuperSuperView.
Any suggestion?
In the SuperSuperView, I had this tap gesture that was entering in conflict with the Button event.
// Tap Gesture
UITapGestureRecognizer tapPageGestureRecognizer = new UITapGestureRecognizer();
tapPageGestureRecognizer.AddTarget(this, new Selector ("HandleTapPageGestureRecognizer:"));
this.View.AddGestureRecognizer(tapPageGestureRecognizer);
The idea is to disable the gesture SuperSuperView gesture when the button event, TouchDown was fired, ... and to re-enable it when the TouchUpInside is fired.
So here is one solution for the problem:
private void SetGrandParentViewGestureEnabled(bool enabled)
{
foreach(UIGestureRecognizer g in this.View.Superview.Superview.GestureRecognizers)
{
g.Enabled = enabled;
}
}
void HandleButtonSubmitTouchDown (object sender, EventArgs e)
{
SetGrandParentViewGestureEnabled(false);
}
void HandleButtonSubmitTouchUpInside (object sender, EventArgs e)
{
// => Do treatments here!
SetGrandParentViewGestureEnabled(true);
}
However, I could have used EventHandler or Action to enable/disable the tap gesture.
EDIT: Here is another function that need to be added as well to re-enable the gesture.
void HandleButtonSubmitTouchUpOutside (object sender, EventArgs e)
{
SetGrandParentViewGestureEnabled(true);
}
I had a similar problem, I ended up using a tap recognizer together with the long press recognizer like this for the button TopRightButton.
var longPressGesture = new UILongPressGestureRecognizer(Action);
TopRightButton.AddGestureRecognizer(longPressGesture);
var tapRecognizer = new UITapGestureRecognizer();
tapRecognizer.AddTarget(() =>
{
//method
});
tapRecognizer.NumberOfTapsRequired = 1;
this.TopRightButton.AddGestureRecognizer(tapRecognizer);
private void Action(){ };

How do i fire an event after a few seconds?

I am making a application where i would like to log in with a face detection. But this is not real, its just to make ik look like its scanning.
So when i press the LOG IN button, the kinect takes my picture and show me the picture, on top of it is showing me in a text that its scanning.
Now i am stuck with the following issue, when i press the login button, the scanning label appears, but i would like to fire an other event that takes me to the next page, the homepage.
So i want the SCANNING label appearing for 3 seconds, and then the page should change.
This is what i tried, i worked with a timer, but that doesnt do annything.
private void ActionButton_Click(object sender, System.EventArgs eventArgs)
{
_main.TakePicture();
identifyBox.Source = _main.source.Clone();
scanningLabel.Visibility = Visibility.Visible;
_storyboard = (Storyboard)FindResource("scanningSB");
//_storyboard.Begin();
Start();
}
private void Start()
{
_tm = new Timer(3000);
_tm.Elapsed += new ElapsedEventHandler(_tm_Elapsed);
_tm.Enabled = true;
}
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == new Timer(3000))
{
((Timer)sender).Enabled = false;
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Okay i removed the if statement, but now it fires every 3 seconds a method.
How can i make it work 1 time.
Ok even this works, now i my ContentPage wont change? It gives me this error: The calling thread cannot access this object because a different thread owns it.
What can be wrong?
I think you can remove condition
if (_tm == new Timer(3000))
and keep it simple
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer)sender).Enabled = false;
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
when you set _tm = new Timer(3000); it will set the time to fire event after 3 seconds..
Change the _tm_Elapse to this:
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == (sender as Timer))
{
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Edit for answering:
"I just want it 1 time to fire after 3 sec"
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == (sender as Timer))
{
_tm.Stop();
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
This is even simpler, and it will only run once per call, the three second timer is built in, and furthermore, it won't disable other program functionalities while it is running:
async Task Start()
{
await Task.Delay(3000);
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}

c# Shutdown a system after holding yes for 5 seconds

I was wondering if anyone knows how to use a dialog box to create a hold down button event. Here is the scenerio:
a user would like to shutdown their system, but because it is critical that they confirm, that user must hold the button for 5 seconds before the action can be done.
I am trying to do it in a yes no scenario ie.
To confirm shutdown please hold "Yes" for 5 seconds.
Anyone done this before able to offer a little help/insight?
Try using a button's Mouse_Down & Mouse_Up event, and a timer (this assumes you're using WinForms).
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (this.timer1.Enabled == false)
{
this.timer1.Interval = 5000;
this.timer1.Enabled = true;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
this.timer1.Enabled = false;
MessageBox.Show("Shutdown!");
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
timer1.Enabled = false;
}
You could capture the button press on 'mousedown', and start a 5-second timer. Once the timer completes, shutdown is initiated. If a 'mouseup' event happens, it could stop and reset the timer.
Sure, handle BOTH the mousedown event and the mouseup event. Start a timer on the mousedown and see how long it has run on the mouseup. Done!
You could do this any number of ways. The first that comes to my mind would be to spin off a thread that waits 5 seconds and is simply aborted if the user's mouse comes back up.
Thread shutdown;
private void button1_MouseDown(object sender, MouseEventArgs e)
{
shutdown = new Thread(()=>ShutDown());
shutdown.Start();
}
private void ShutDown()
{
Thread.Sleep(5000);
Console.Write("5 seconds has elapsed");
// Do something.
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
if (shutdown != null)
{
shutdown.Abort();
shutdown = null;
}
}
Low overhead and you're not adding additional supporting controls for something this simple.
Why bother when you can just use getAsyncKeyState()? Tell them to hold down 'y' for 5 seconds. You can find a reference here: http://www.pinvoke.net/default.aspx/user32.getasynckeystate
Or you can do it your way and start a timer on MouseDown, then on MouseUp, end the timer and then see if it's more or less than 5 seconds. http://msdn.microsoft.com/en-us/library/system.windows.forms.control.mousedown%28VS.71%29.aspx
You can use the Form.MouseDown events do detect that the user has pressed the mouse button. In the event handler, check to see if cursor is over the button or not (the event is passed in the coordinates of the cursor). You can then enable a timer which will tick in 5 seconds, and perform the shutdown when the timer ticks.
When the user first clicks YES, start a timer that repeatedly checks if the mouse location is inside of the button. After 5 seconds has elapsed, proceed with the shutdown. If the user moves the mouse out of the button, stop the timer.
private DateTime mouseDownTime;
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseDownTime = DateTime.Now;
}
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
if (mouseDownTime.AddSeconds(5) < DateTime.Now)
MessageBox.Show("You held it for 5 seconds!");
}
You can set up a timer on the MouseDown event, and if the mouse capture changes (check the MouseCaptureChanged event) to false before the timer event fires, cancel the timer.

Categories

Resources