Touch DragDrop - Blocks all inputchanges - c#

I'm trying to handle a touch drag and drop operation with DragDrop.DoDragDrop. As I read in this post (I, too, can't use the surface drag framework for this) I also have to implement a QueryContinueDragHandler, which I have with the following implementation:
(in the mainwindow constructor)
this.QueryContinueDrag += (obj, e) =>
{
if(_touchDevice.IsActive)
e.Action = DragAction.Continue;
else
e.Action = DragAction.Drop;
};
(on some TouchDown Eventhandler)
Object data = new Object();
_touchDevice = e.TouchDevice;
DragDrop.DoDragDrop(this, data, DragDropEffects.Link);
However, with this the drag action never ends, as _touchDevice.IsActive will always be true, no matter if the TouchDevice is actually still "touching".
This question is also related to mine, but I find the answer unsatisfactory and I didn't want to turn the thread into a zombie.
How can I detect when/if the TouchDevice will be inactive?

Related

How to properly send different parameters down for a standard Button Click

I know this has to have an easy answer, but I'm utterly failing to fathom the wealth of information on custom events, event handlers, and delegates. I have a custom messagebox class. I am trying to add the capability to do something based off of the state of a check box if the OK button is clicked. The buttons and the checkbox are added dynamically based upon input into a static Show method somewhat like the following:
if (!String.IsNullOrWhiteSpace(suicideCheckboxID))
{
suicideCheckBox = new CheckBox();
suicideCheckBox.AutoSize = true;
suicideCheckBox.Text = "Do not show this message again.";
suicideCheckBox.Location = new Point(xMargin, label.Bottom + yMargin);
suicideCheckBox.Checked = false;
suicideCheckBoxHeight = suicideCheckBox.Height;
form.Controls.Add(suicideCheckBox);
}
Button okButton = NewButton(DialogResult.OK, scaleFactor);
int x = (form.Width - okButton.Width) / 2;
okButton.Location = new Point(x, buttonYPosition);
form.Controls.Add(okButton);
form.AcceptButton = okButton;
form.CancelButton = okButton;
That's not the exact code, but it's fairly representative. My impulse is to use okButton.Clicked += new EventHandler(OKButton_clicked), but if I do that, the event generated only carries arguments for object sender and EventArgs e and I really need it to operate off of the state of the checkbox and an additional piece of text to indicate which messagebox is being shown so that the values can be stored in the registry.
My first attempt was to do something like okButton.Clicked += processSuicideCheckbox(suicideCheckboxID, suicideCheckBox);, but that seems to just process the contents and allow one to return an EventHandler that points to a method with the signature of object sender and EventArgs e. What am I missing here? What is the best way to pass in the arguments actually relevant to me?
You don't get to choose what is in the event handler for the Click event. Microsoft has already done that. You are stuck with the (object sender, EventArgs e) signature.
You do have a couple options:
Simply store the state in the class itself; the event handler will have access to it because it is inside the class.
Utilize a closure to do the same thing:
myButton.Click += (s, e) => ActualFunction(checkBox1.Checked);
Note that using the closure (via a lambda expression) is just hiding the details of maintaining this state (creating the class-level variables).

What is the best way to mask an UI Event?

I am working with a DataGridView, and I use the CellValueChanged event.
I dont want this event to be triggered when I change a cell value by the code. However, I want it to be triggered when the user edits it.
That's why I enclose my cell value change operations with the following code :
void changeCellOperation()
{
dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged);
...
cell.Value = myNewCellValue
...
dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged);
}
I ended to have several differents functions where my DataGridView cells are updated this way.
Because these functions are called from different places and can be nested, I cannot afford to keep this code as is to avoid event unwanted event reactivation.
So I ended up this way :
int valueChangedEventMask = 0;
void changeCellOperation()
{
valueChangedEventMask++;
...
cell.Value = myNewCellValue
...
valueChangedEventMask--;
}
void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (valueChangedEventMask > 0)
return
...
}
This works fine. Also when the calls are nested, including inside the event itself.
But the CellValueChanged event is now fired too many times for no reasons.
Because I often have to cope with this pattern, I am looking for a solution that can be applicable generally for Events in UIs, not only the DataGridView.
So my question is:
What is the best tip to mask UI Events correctly and avoid unnecessary Events fires ?
CellValueChanged is not an UI event, but a property changed event. That means you can not use it to distinguish user input from programmatic change. You can always use subscriber/unsucscribe or flag+/- or BeginEdit/EndEdit-similar technique, but maybe you have to find another (better) approach. To example, in case of checkbox you can use Click event instead of Changed, because (surprise!) it will tell you when the user click it and otherwise safely change value of Checked programmatically.
In case of DataGridView easiest would be to use Changed with some flag (which will be set when edit begins and reset when ends - see, CellBeginEdit/CellEndEdit ).
You could use CellEndEdit instead of CellValueChange. I don't know what your method dgv_CellValueChanged does, just be careful that CellEndEdit is fired every time you exit the edit mode for the cell, even if its value has not been changed. This means that you have to keep trace of the current values of your cells if you don't want the method to be executed when the value doesn't change.
I would avoid events related with the mouse such as CellClick because your users could use just the keyboard.
Anyway I usually avoid this kind of problems by separating the logic from the user interface, i.e. I write a separate class which is bound to the form. Take a look at MVVM (you can implement your own version in WinForms if you want) or the good old MVC.
I ended up mixing both solutions in a very simple one. I use a counter and I only hook/unhook the events I want to mask.
EventMask valueChangedEventMask;
// In the class constructor
valueChangedEventMask = new EventMask(
() => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
() => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
);
// The value change operation I want to hide from the event
void changeCellOperation()
{
valueChangedEventMask.Push();
...
cell.Value = myNewCellValue
...
valueChangedEventMask.Pop();
}
// The class
public class EventMask
{
Action hook;
Action unHook;
int count = 0;
public EventMask(Action hook, Action unHook)
{
this.hook = hook;
this.unHook = unHook;
}
public void Push()
{
count++;
if (count == 1)
unHook();
}
public void Pop()
{
count--;
if (count == 0)
hook();
}
}

WPF Multitouch - How to get all touchpoints?

We are evaluating touchscreen keyboards and for one we need to track 10 fingers at the same time. The problem is that the touchscreen driver is very wonky (and there is no fixed version). It sends out 2500+ events for the FrameReported event each second for so many fingers. There is just no way to handle all those, even if we discard 90% at the beginning. It's simply impossible to keep up and do anything meaningful with the data.
Instead of System.Windows.Input.Touch.FrameReported, I also tried to use the (Preview) TouchMove events of the window; Same problem here.
So now I wanted, instead of using events, to poll in a separate Thread, but I cannot find information on how to get all the current touchpoints.
The only thing I found is a WinForms hack, but that isn't an option, since then I will be unable to render any WPF controls in my window.
Any solutions?
Edit 1:
This is the code, that handles all the move events:
private void UserControlTouchMove(object sender, TouchEventArgs e)
{
//Update Position of the corresponding point
var touch = e.GetTouchPoint(this);
var id = touch.TouchDevice.Id;
e.Handled = true;
var position = touch.Position;
//update finger on display, quick and dirty
if (m_ShowFingers)
{
foreach (var finger in m_Fingers)
{
if (id == (int)finger.DataContext)
{
finger.RenderTransform = new TranslateTransform(position.X - HalfFingerSize, position.Y - HalfFingerSize);
break;
}
}
}
}

Button Screen Change in Android Mono using delegates

The following code is from the C# portion of my Android Mono application. It is going to eventually be the GUI for a multimeter simulator, but right now just displays text. It is rather straight forward:
-Click one of the buttons to go to that meter (voltmeter, ammeter, ohmmeter)
-Click the "re-scan" button and a TextView tells you how many times you clicked that button.
-Click one of the other meter buttons or the home button to switch views
That much is working flawlessly. Unfortunately, once I switch views, the buttons cease to work. Below is the code for the Ohm button and the Amp button. The Ohm button is the 'complete' one that brings up views of all of the other screens. For testing purposes, I was going to the amp screen but when I go there, its re-scan button does nothing. None of the buttons do anything.
I am fairly certain that the issue is my use of the delegate commands, but none of my research has led me in any way towards a solution.
I can provide more of the main code and the XML code if needed.
ampButton.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
Button ampButtonData = FindViewById<Button>(Resource.Id.CurrentButtonamp);
TextView ampData = FindViewById<TextView>(Resource.Id.ampdata);
ampButtonData.Click += delegate
{
ampData.Text = string.Format("{0} clicks!", count2++);
};
Button amp2volt = FindViewById<Button>(Resource.Id.Amp2VoltButton);
Button amp2ohm = FindViewById<Button>(Resource.Id.Amp2OhmButton);
Button amp2home = FindViewById<Button>(Resource.Id.Amp2HomeButton);
};
ohmButton.Click += delegate
{
SetContentView(Resource.Layout.OhmScreen);
Button ohmButtonData = FindViewById<Button>(Resource.Id.CurrentButtonohm);
TextView ohmData = FindViewById<TextView>(Resource.Id.ohmdata);
ohmButtonData.Click += delegate
{
ohmData.Text = string.Format("{0} clicks!", count3++);
};
Button ohm2amp = FindViewById<Button>(Resource.Id.Ohm2AmpButton);
Button ohm2volt = FindViewById<Button>(Resource.Id.Ohm2VoltButton);
Button ohm2home = FindViewById<Button>(Resource.Id.Ohm2HomeButton);
ohm2amp.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
};
ohm2volt.Click += delegate
{
SetContentView(Resource.Layout.VoltScreen);
};
ohm2home.Click += delegate
{
SetContentView(Resource.Layout.Main);
};
};
I think your problem is that you are replacing the entire view each time - so the button instances are changing.
What happens inside SetContentView is that the InflatorService gets asked to create a brand new set of UI objects based on the passed in XML, the existing UI is wiped clean and then those new UI objects are put in their place.
It doesn't matter if the new UI objects happen to have the same resource identifiers as the old objects - they are still separate instances.
If you want to continue using your current approach, then you need to rewire all your events after each SetContentView - e.g.
ohm2amp.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
RewireEvents();
};
with
private void RewireEvents()
{
var ohm2home = FindViewById<Button>(Resource.Id.ohm2home);
ohm2home.Click += { /* todo */ };
// etc
}
alternatively, maybe consider a different UI:
e.g. you could change the Visibility on different child layouts rather than calling SetContentView to replace everything
e.g. or you could use multiple activities (or tabs) instead of a single activity
Hope that helps

What is the proper way to determine the end of a mouse drag using Rx?

I am slowly learning how to use Reactive Extensions for .NET with WPF. There a few beginner examples about how simple it is to write drag-drop or drawing routines but they are all extremely simple. I'm trying to go one step further and it's not obvious to me what the "proper" way is.
The examples all show how you can define streams of events from MouseDown, MouseMove, and MouseUp
var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(..., "MouseDown")
select evt.EventArgs.GetPosition(...);
var mouseMoves = from evt in Observable.FromEvent<MouseEventArgs>(..., "MouseMove")
select evt.EventArgs.GetPosition(...);
var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(..., "MouseUp");
And then how you can easily do things during a MouseDrag (this displays the co-ordinates of the rectangle created from the starting drag point to the current mouse position)
var mouseDrag = from start in mouseDown
from currentPosition in mouseMoves.TakeUntil(mouseUp)
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
mouseDrag.Subscribe(x =>
{
Info.Text = x.ToString();
});
My question is, what is the "proper" way to accomplish a task at the end of the mouse drag? Originally, I thought I could do something like this:
mouseDrag.Subscribe(
onNext: x =>
{
Info.Text = x.ToString();
},
onCompleted: () =>
{
// Do stuff here...except it never gets called
});
Reading more of the documentation, though, it seems that onCompleted is called when there is no more data (ever) and when the object can be disposed.
So the first option that seems plausable is subscribing to the mouseUp event and doing something there.
mouseUp.Subscribe(x =>
{
// Do stuff here..
}
But then at this point, I may as well go back to just use the "normal" MouseLeftButtonUp event handler.
Is there another way to determine when the mouseDrag is "completed" (or when the TakeUntil(mouseUp)) occurs and perform some action then?
The sequence never completes because the source (MouseDown) never completes (it is an event). It's worth pointing out that a IObservable cannot call OnComplete of a subscriber more than once, it's part of the contract (OnNext* (OnCompleted|OnError)?).
To find out when themouseMove.TakeUntil(mouseUp) sequence completes, you'll need to hook into the call to SelectMany:
public static IDisposable TrackDrag(this UIElement element,
Action<Rect> dragging, Action dragComplete)
{
var mouseDown = Observable.FromEvent(...);
var mouseMove = Observable.FromEvent(...);
var mouseUp = Observable.FromEvent(...);
return (from start in mouseDown
from currentPosition in mouseMove.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
).Subscribe(dragging);
}
Then you can use it like so:
element.TrackDrag(
rect => { },
() => {}
);
For the interest of clarity, here is the LINQ expression using the underlying extension methods:
return mouseDown.SelectMany(start =>
{
return mouseMove
.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
.Select(currentPosition => new Rect(...));
})
.Subscribe(dragging);
That is, for each value from mouseDown a new sequence will be subscribed to. When that sequence completes, call dragComplete().

Categories

Resources