Ignore Single Click in WPF - c#

I have a user control wherein I would like to do something different in the case of a single click vs. double click. I'm handling mouseLeftButtonDown event as such:
private void cueCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (e.ClickCount)
{
case 1:
{
cueCanvas.Focus();
e.Handled = true;
_mouseStartPoint = Mouse.GetPosition(null);
break;
}
case 2:
{
double pos = Mouse.GetPosition(cueCanvas).X;
TimeSpan time = ConvertFromPosition(pos);
AddNewEvent(time);
e.Handled = true;
break;
}
}
}
The problem is that WPF doesn't really care how many times you've clicked the mouse so I get the first click event even though I'm about to get a second one (in the case of a double-click). Has anyone come up with a way to work around this? I realize I could try to get clever and set some timers such that the first click gets "canceled" in the event that the second one comes in (I realize this is what the framework would be doing anyway). If that's the only answer, does anyone know how we query the system for the appropriate click delay?

To my knowledge, there's really no way to do this. The problem is that you're fighting reality. :-) In reality, there are 2 clicks to a double-click. You want single click to be ignored if quickly followed by a double-click.
You'll have wait a short interval to see if it's followed by a second click. You can query for that interval using the SystemInformation class from WinForms, or just call the Win32 API directly.

Related

How to detect game controller button presses (ABXY) in UWP apps

I've developed a UWP app that could potentially run on XBOX.
I want to detect whether the buttons on the gamepad controller have been pressed (either A B X or Y).
I think I need to be using the click event? If it is on the click event, what do I need to check in the click event?
Looking at this post which determines whether a trigger has been pressed..
Controller support for Xbox one in Windows UWP
/*
* Note: I have not tested this code.
* If it is incorrect, please do edit with the fix.
*/
using Windows.Gaming.Input;
// bla bla bla boring stuff...
// Get the first controller
var controller = Gamepad.Gamepads.First();
// Get the current state
var reading = controller.GetCurrentReading();
// Check to see if the left trigger was depressed about half way down
if (reading.LeftTrigger == 0.5;)
{
// Do whatever
}
I presume there is an equivalent way of checking whether one of the ABXY buttons have been pressed?. I shall check next time I get chance.
On another note, this blog post looks really useful for people starting out with developing UWP for Xbox One http://grogansoft.com/blog/?p=1278
Update :
Looks like I can call GetCurrentReading() to get a GamepadReading structure. and from that get the state of the GamepadButtons.
The KeyDown event from CoreWindow or any other UWP control will be fired even when user clicks the gamepad buttons. You can find values such as GamepadA and GamepadB in the VirtualKey enumeration so basic method for checking their pressing could look like this:
private void CoreWindow_KeyDown(CoreWindow sender, KeyEventArgs args)
{
if (args.Handled)
{
return;
}
switch (args.VirtualKey)
{
case VirtualKey.GamepadA:
// Gamepad A button was pressed
break;
case VirtualKey.GamepadB:
// Gamepad B button was pressed
break;
case VirtualKey.GamepadX:
// Gamepad X button was pressed
break;
case VirtualKey.GamepadY:
// Gamepad Y button was pressed
break;
}
}
You have to subscribe the event (for example in constructor):
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;

How to get a XAML Button Tapped event to fire for all taps?

I have a couple buttons that act as +/- stepper controls for a quantity. The default behavior of Windows is to absorb a tap if it is considered a double tap and will not fire a second tapped event:
If a user interaction also fires DoubleTapped, Tapped will fire first to represent the first tap, but the second tap won't fire an additional Tapped.
But this provides a very poor user experience for a stepper button. The user should be able to tap it as quickly as they want and it should increment the counter each time, not every other tap as it is currently doing.
The strange thing is I can't find how to do this anywhere. At first I thought I had it with the IsDoubleTapEnabled property, but this sadly does buck all. Exact same behavior whether it's turned on or off.
There is a much simpler way - use a built-in RepeatButton instead. It even handles pressed state automatically so you can press and hold on it to change the values continuously.
One way of achieving this would be calling the single tapped event in the double tap handler. It can only be done if you don't want the arguments passed in through the event.
But it's always nice to have the data even though it's not required at the moment, you would never know what and when do you want to scale to so you could create a simple BridgeClass as I like to call it, that helps to transfer data between the two events and then calls a method. The below code does exactly the same:
In the code behind:
int counter = 0;
private void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
MyTappedClass SingleTapInstance = new MyTappedClass()
{
Handled = e.Handled,
OriginalSource = e.OriginalSource,
PointerDeviceType = e.PointerDeviceType
};
HandleBothTapEvents(SingleTapInstance);
}
private void Button_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
MyTappedClass DoubleTapInstance = new MyTappedClass()
{
Handled = e.Handled,
OriginalSource = e.OriginalSource,
PointerDeviceType = e.PointerDeviceType
};
HandleBothTapEvents(DoubleTapInstance);
}
private void HandleBothTapEvents(MyTappedClass TapData)
{
if (TapData != null)
{
counter++;
Hello.Content = counter.ToString();
}
else
throw new Exception("No Tap event data transfered");
}
The supporting class
and the BridgeClass where ever you want to put it. My advice put it in the Helpers Folder (if you have any).
public class MyTappedClass
{
public object OriginalSource { get; set; }
public PointerDeviceType PointerDeviceType { get; set; }
public bool Handled { get; set; }
}
Note: This is just a quick workaround that'll work really well I don't know if there's a conventional way to achieve this but then there is no single correct answer in programming so I guess you can use this. The functionality can be made more versatile by using Interfaces to transfer the data between the two events instead of a BridgeClass as Interfaces are designed to do the same.
The IsDoubleTapEnabled property should do what you want but many people often miss the fact that this is automatically set to true if you have an event handler set for the DoubleTap event on control. Remove the eventhandler if you have one set and you should be able to get all click events in rapid succession.

c# wp8 timepicker deactivation default eventhandler

I want to have in my application timepicker, which when pressed long set the current time. Setting the time works correctly (hold event), but it also opens the screen for entering the time (just as when I click on timepicker only). Is it possible to change this behavior? When clicked, I would like to open a screen to set the time as normal, but not when long pressed. Thanks for the reply.
Microsoft.Phone.Controls.TimePicker time = new TimePicker();
time.FontSize = 18;
time.Height = 50;
time.Hold += time_Hold;
private void time_Hold(object sender, System.Windows.Input.GestureEventArgs e)
{
((TimePicker)sender).Value = DateTime.Now;
}
There seems to be no easy way to accomplish this.
You can find the source code of the TimePicker here:
https://phone.codeplex.com/SourceControl/latest#Microsoft.Phone.Controls.Toolkit/DateTimePickers/TimePicker.cs
Maybe subclassing this class and overriding DateTimePickerBase::OnApplyTemplate() could be a way to proceed...
I didn't get the question correctly but as far as I understand that you want to delay the launch. Use DispatcherTimerand wrap the code where you want in the callback.
I think you can disable the click event or override it not to show when clicked.

Second TextBox showing same Text Selection as first

Long time listener, first time caller here. I'm having a strange issue with the TextBox in WinRT C#/XAML that I hope someone may be able to help me with.
Basically, I'm working on creating a Custom Control that essentially requires a second TextBox to be a copy of the first, including showing the same Text, and showing the same Selected Text. Obviously for the Text requirement I simply respond to the TextChanged event on the first TextBox and set the Text of the second TextBox to the Text from the first, which works great.
For the Selected Text requirement I started with a similar solution, and my code for this is as follows:
void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
This seemed to work pretty well when initially used with a mouse:
But I'm having a problem when selecting text with Touch. I double-tap within the TextBox to create the first "anchor" as you do in Touch, then drag to begin the selection; but I only ever manage to select a single character normally before the selection stops. The TextBox doesn't lose focus exactly, but the behaviour is similar to that; the selection anchors disappear and I can't continue selecting anything unless I re-double-tap to start a new selection. If I remove the code to select text in TextBox2 then the Touch selection behaves perfectly in TextBox1.
I've been trying to fix this for a while and cannot, I'm not sure if I can get the desired behaviour with WinRT TextBoxes. Does anyone have any ideas? Or perhaps another way to implement a solution with two TextBoxes with this behaviour?
Thanks a lot.
So this is far from an answer, but discovered a few things that maybe will help you or others come up with a potential workaround. Apologies if these are things you've already seen and noted.
First, it's not the call to TextBox2.Select() that's the problem per se. This for instance, works fine for me
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(3, 5);
}
unfortunately, using start and length versus the hard-coded 3 and 5, that is, the following, DOES NOT WORK:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(start, length);
}
I also discovered that I could select TWO characters if I started from the end, but only one from the beginning. That got me to thinking about dispatching the call to set the second selection:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low,
() => TextBox2.Select(start, length));
}
Now I can select 2 from the front and 3 and sometimes 4 from the back. Took it a step further, and was able to select as many as six or seven with a really fast swipe.
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunIdleAsync((v) => Highlight());
}
public void Highlight()
{
TextBox2.Select(TextBox1.SelectionStart, TextBox1.SelectionLength);
}
Seems like the trick to working around this is not setting TextBox2 until whatever vestiges of the TextBox1 SelectionChanged event have completed.
This may be worth registering on Connect.
Mine is only a partial solution as well.
I did some debugging and noticed that the SelectionChanged event is fired throughout the text selection process. In other words, a single finger "swipe" will generate multiple SelectionChanged events.
As you found out, calling TextBox.Select during a text selection gesture affects the gesture itself. Windows seems to stop the gesture after the programmatic text selection.
My workaround is to delay as long as possible calling the TextBox.Select method. This does work well, except for one edge case. Where this method fails is in the following scenario:
The user begins a select gesture, say selecting x characters. The user, without taking their finger off the screen, pauses for a second or two. The user then attempts to select more characters.
My solution does not handle the last bit in the above paragraph. The touch selection after the pause does not actually select anything because my code will have called the TextBox.Select method.
Here is the actual code. As I mentioned above, there are multiple selection changed events fired during a single selection gesture. My code uses a timer along with a counter to only do the programmatic selection when there are no longer any pending touch generated selection changed events.
int _selectCounter = 0;
const int SELECT_TIMER_LENGTH = 500;
async private void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
// _selectCounter is the number of selection changed events that have fired.
// If you are really paranoid, you will want to make sure that if
// _selectCounter reaches MAX_INT, that you reset it to zero.
int mySelectCount = ++_selectCounter;
// start the timer and wait for it to finish
await Task.Delay(SELECT_TIMER_LENGTH);
// If equal (mySelectCount == _selectCounter),
// this means that NO select change events have fired
// during the delay call above. We only do the
// programmatic selection when this is the case.
// Feel free to adjust SELECT_TIMER_LENGTH to suit your needs.
if (mySelectCount == _selectCounter)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
}

Sorting enum array more efficiently

Currently I have some buttons on my Winform that need to be disabled/enabled at various points depending what the user clicks.
The first draft I made was
button1.Enabled = false;
button2.Enabled = false;
To disable 2 buttons, which is obviously a horrible way of doing this, as there are currently a lot more than 2 and possibly more to come as this is still in development. So I need to have a way of easily changing a selection of buttons on the form.
Then I came up with this
private enum Buttons { Button1, Button2 } // etc with all buttons - that are named :)
private void DisableButtons(params Buttons[] buttons)
{
foreach (Buttons button in buttons)
{
switch (button)
{
case Buttons.Button1:
button1.Enabled = false;
break;
case Buttons.Button2:
button2.Enabled = false;
break;
}
}
}
Which I still wasn't overly happy with. I could scrap the switch-case and foreach for
private void DisableButtons(params Buttons[] buttons)
{
button1.Enabled = buttons.Contains(Buttons.Button1) ? false : true;
}
for each button but I just think there must be a better way.
Any ideas on how I could do this more efficiently?
Thanks
You can shorten your last code line to:
button1.Enabled = !buttons.Contains(Buttons.Button1);
Alternative solution
Or you can use the Tag property of each button to set a enum value for each button.
button1.Tag = Buttons.Button1;
button2.Tag = Buttons.Button2;
button3.Tag = Buttons.Button3;
// etc
Than you can do it for all buttons in a for loop:
var buttons = <all buttons, todo>
foreach (var button in buttons) {
button.Enabled = !button.Contains((Buttons)button.Tag));
}
I would suggest that you don't infact want to make a function that flexibly enables and disables any combination of buttons because you don't yet percieve how your Form will work. Granted, this may save you a few lines of code but, it won't impart any contextual information to the next developer that maintians your code. Neither will it run faster than directly setting the state of the controls.
I woud make a single function that is called whenever your Form changes state, that takes all possible parameters that pertain to your Forms state. Then I would decode those parameters and explicitly setup the state of the controls on the form, by name, in a single pass, using tranditional switch and if statments.
This central function will make it clear to you and future developers how the state of you form changes and how controls are expected to behave. It won't slow down the performance of you code with an unecessary level of abstraction.

Categories