I have a PushPin object I have hooked up to a handful of Touch / Stylus / Mouse events:
pp.MouseDown += pp_MouseDown;
pp.TouchDown += pp_TouchDown;
pp.TouchUp += pp_TouchUp;
pp.StylusDown += pp_StylusDown;
pp.StylusUp += pp_StylusUp;
Handlers
void pp_MouseDown(object sender, MouseButtonEventArgs e)
{
PushPinUpOrDown(sender);
e.Handled = true;
}
private void pp_TouchDown(object sender, TouchEventArgs e)
{
var pushpin = (sender as Pushpin);
pushpin.CaptureTouch(e.TouchDevice);
e.Handled = true;
}
void pp_StylusDown(object sender, StylusDownEventArgs e)
{
var pushpin = (sender as Pushpin);
pushpin.CaptureStylus();
e.Handled = true;
}
void pp_StylusUp(object sender, StylusEventArgs e)
{
var pushpin = (sender as Pushpin);
e.Handled = true;
if (pushpin != null && e.StylusDevice.Captured == pushpin)
{
PushPinUpOrDown(sender);
pushpin.ReleaseStylusCapture();
}
}
void pp_TouchUp(object sender, TouchEventArgs e)
{
var pushpin = (sender as Pushpin);
e.Handled = true;
if (pushpin != null && e.TouchDevice.Captured == pushpin)
{
PushPinUpOrDown(sender);
pushpin.ReleaseTouchCapture(e.TouchDevice);
}
}
but when I touch my PushPin firstly the StylusDown event fires then followed by the MouseDown. The TouchDown event I would expect to fire never fires.
Why is this? is this a problem with my program or my monitor?
Do I need both Stylus and Touch events?
(I am using a touch enabled monitor not tablet or anything)
So from here I got the code:
void pp_StylusDown(Object sender, StylusEventArgs e)
{
if (sender != null)
{
//Capture the touch device (i.e. finger on the screen)
e.StylusDevice.Capture(sender as Pushpin);
}
}
void pp_StylusUp(Object sender, StylusEventArgs e)
{
var device = e.StylusDevice;
if (sender != null && device.Captured == sender as Pushpin)
{
(sender as Pushpin).ReleaseStylusCapture();
PushPinUpOrDown(sender, true);
e.Handled = true;
}
}
which stopped the MouseDown event firing. The main reason (strangely) was removing e.Handled = true from pp_StylusDown fixed the problem
Still doesn't explain why the stylus event fires when touching the screen
Related
I have a borderless, transparent Window with only one Button.
My fancy button looks like this
The expected behaviour is:
When I click and drag the Button, the Button must follow the cursor.
When I only click on the Button, the MouseDown, PreviewMouseDown events or Command binding should raise.
At first I tried to call the DragMove() on PreviewMouseDown event, but that blocks the click events. Now my idea is: I set a 100ms delay after mouse down. If the time passed, than the button wil be dragging, otherwise it was just a click.
Code
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_dragging && e.LeftButton == MouseButtonState.Pressed)
{
var currentpos = e.GetPosition(this);
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
await Task.Delay(100, cancellation.Token).ContinueWith(task =>
{
_dragging = !task.IsCanceled;
});
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
}
cancellation?.Cancel();
}
Basically it works, but has some bugs:
When I hold down the mouse button for longer time and release, then the click won't work, because after 100ms the dragging will be active.
After I dragged the button, and click anywhere outside the Button and Window control, the PreviewMouseDown and PreviewMouseUp events are raised. I don't know why??
Does somebody have any better solution?
For your first problem:
You must handle the case when your button is pushed down but not moved. I think a better way to do this (instead of the 100ms delay) would be to specify a minimum threshold of movement above which the dragging will start.
You could do it like this:
private const double _dragThreshold = 1.0;
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
var currentpos = e.GetPosition(this);
var delta = currentpos - startpos;
if ((delta.Length > _dragThreshold || _dragging) && e.LeftButton == MouseButtonState.Pressed)
{
_dragging = true;
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
}
For your second problem: the button will capture the mouse on the mouse down event.
You need to release the captured mouse with the ReleaseMouseCapture method when you are done with your drag.
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
var button = sender as Button;
button.ReleaseMouseCapture();
}
cancellation?.Cancel();
}
This may be a basic question but I just started using WPF and I am having troubles trying to do a simple drag and drop.
I created this ToolboxButton class:
public class ToolboxButton : Button
{
private bool _isDragging = false;
private Point _startPoint;
public ToolboxButton(string content)
{
Content = content;
HorizontalAlignment = HorizontalAlignment.Stretch;
Height = 30;
Loaded += ToolboxButton_Loaded;
}
void ToolboxButton_Loaded(object sender, RoutedEventArgs e)
{
PreviewMouseLeftButtonDown += ToolboxButton_PreviewMouseLeftButtonDown;
PreviewMouseMove += ToolboxButton_PreviewMouseMove;
}
void ToolboxButton_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !_isDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDrag(e);
}
}
}
void ToolboxButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
private void StartDrag(MouseEventArgs e)
{
_isDragging = true;
DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
DragDrop.DoDragDrop(e.Source as ToolboxButton, data, DragDropEffects.Move);
_isDragging = false;
}
}
This button is added in a stackpanel like so:
ToolboxButton btnAddButton = new ToolboxButton("Button");
_toolboxView.Children.Add(btnAddButton); // _toolboxView is a stackpanel
And I have a Canvas with the following code:
public class DesignerView : Canvas
{
public DesignerView()
{
AllowDrop = true;
DragOver += DesignerView_DragOver;
Drop += DesignerView_Drop;
PreviewDragOver += DesignerView_PreviewDragOver;
}
void DesignerView_PreviewDragOver(object sender, DragEventArgs e)
{
MessageBox.Show("previewdragover");
}
void DesignerView_DragOver(object sender, DragEventArgs e)
{
MessageBox.Show("dragover");
if (!e.Data.GetDataPresent(typeof(ToolboxButton)))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
}
void DesignerView_Drop(object sender, DragEventArgs e)
{
MessageBox.Show("drop");
if (e.Data.GetDataPresent(typeof(ToolboxButton)))
{
ToolboxButton droppedThingie = e.Data.GetData(typeof(ToolboxButton)) as ToolboxButton;
MessageBox.Show("You dropped: " + droppedThingie.Content);
}
}
public UIElement GetView()
{
return this;
}
}
Both Canvas and StackPanel are added in the main window like so:
Grid contentGrid = new Grid();
Content = contentGrid;
contentGrid.Children.Add(_toolboxView.GetView());
contentGrid.Children.Add(_designerView.GetView());
None of the MessageBoxes ever fire and I can't find out why. The cursor changes to the "Cannot pin", a dark circle with a diagonal line inside.
Am I missing something ? I want everything to be done in the code without XML.
Maybe I have to do something on the StackPanel but I tried the code of ToolboxButton there and it didn't work either.
As I can see you done all job, just DesignerView_drop left to correct.
use sender object to grab dragged object (in this example button)
void DesignerView_Drop(object sender, DragEventArgs e)
{
MessageBox.Show("drop");
Button btn = (Button)sender;
contentGrid.Children.Add(btn);
}
I have Form with textboxes and they have MouseEvent(MouseMove, MouseDown) and they are enabled on form load, but my question is how to call them just when i Click the Edit Button, so just then the textboxes can move?
My code:
private void textBox_MouseMove(object sender, MouseEventArgs e)
{
TextBox txt = sender as TextBox;
foreach (TextBox text in textBoxs)
{
if (e.Button == MouseButtons.Left)
{
if (txt.Name == text.Name)
{
txt.Left = e.X + txt.Left - MouseDownLocation.X;
txt.Top = e.Y + txt.Top - MouseDownLocation.Y;
}
}
}
}
private void textBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
MouseDownLocation = e.Location;
}
}
private void btnEdit_Click(object sender, EventArgs e)
{
btnEdit.Visible = false;
btnPrint.Visible = false;
btnSave.Visible = true;
//Want to call mouse function here.
}
any suggestions?
Instead of hooking the events from the Visual Studio designer, you should manually hook the events in the btnEdit_Click handler method by adding this line:
textboxname.MouseMove += new MouseEventHandler(textBox_MouseMove);
Then unhook the event when your save button is clicked (I'm assuming you have some method btnSave_Click) by doing the following:
textboxname.MouseMove -= new MouseEventHandler(textBox_MouseMove);
The same goes for your MouseDown event.
If I understand your post, you want the textbox functionality to become "active" after you click the btnEdit button?
You could set a flag in btnEdit_Click, and only process the functionality in the other functions if that flag is true
Or, maybe add the event in the btnEdit_Click function, e.g.
private void btnEdit_Click(object sender, EventArgs e)
{
btnEdit.Visible = false;
btnPrint.Visible = false;
btnSave.Visible = true;
//Want to call mouse function here.
textBox.MouseDown += new MouseEventHandler(textBox_MouseDown);
}
But remove that extra line from where it currently exists in your code.
I have a control (basically an ON/OFF toggle switch) which I want the user to press for at least one second before switching states. I can measure the time between the mouse down and mouse up events on the control and make sure the mouse never leaves the control while down, but wanted to know:
Is there was a better method of establishing that the "click" on that control satisfies a minimum time?
There is no simpler way, you must do the steps you have described.
But, this "behavior" can be implemented in a general way - so it can be reused multiple times.
Here is an example of such implementation:
public class LongClick
{
public static void Attach(Control Control, EventHandler Handler)
{
var LC = new LongClick { Control = Control, Handler = Handler };
Control.MouseDown += LC.ControlOnMouseDown;
Control.MouseMove += LC.ControlOnMouseMove;
Control.MouseUp += LC.ControlOnMouseUp;
}
private Control Control;
public EventHandler Handler;
private DateTime? MDS;
private void ControlOnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) MDS = DateTime.Now;
}
private void ControlOnMouseMove(object sender, MouseEventArgs e)
{
if (MDS == null) return;
if (e.X < 0) MDS = null;
if (e.X > Control.Width) MDS = null;
if (e.Y < 0) MDS = null;
if (e.Y > Control.Height) MDS = null;
}
private void ControlOnMouseUp(object sender, MouseEventArgs e)
{
if (MDS == null) return;
if (e.Button != MouseButtons.Left) return;
var TimePassed = DateTime.Now.Subtract(MDS.Value);
MDS = null;
if (TimePassed.TotalSeconds < 1) return;
if (Handler == null) return;
Handler(Control, EventArgs.Empty);
}
}
And the usage is:
private void Form1_Load(object sender, EventArgs e)
{
LongClick.Attach(button1, button1_LongClick);
}
private void button1_LongClick(object sender, EventArgs e)
{
MessageBox.Show("button1 long clicked!");
}
There are other variations of the implementation, one of them would be to override the control class (it is even simpler than this one).
Does anyone knows how can I validate if the event is raised because the DragAction changed to cancel, or because it actually object was dragged out from the control?
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.dragleave.aspx
[edit]
In this part I start the drag in the object being dragged.
void control_MouseDown(object sender, MouseEventArgs e)
{
var wasClicked = WasClicked;
if (wasClicked != null)
{
WasClicked(this, EventArgs.Empty);
}
// Select this UC on click.
if (IsSelected == false)
IsSelected = true;
else
IsSelected = false;
if (IsSelected == true)
{
_beingDragged = true;
DoDragDrop(this, DragDropEffects.Move);
}
return;
}
This part the event validates when the object being dragged leaves the FlowLayoutPanel.
private void LocationControl_DragLeave(object sender, EventArgs e)
{
foreach (Control c in Controls)
{
if (((PersonDragDrop)c).beingDragged == true)
{
((PersonDragDrop)c).LeaveLocation();
DeletePerson((PersonDragDrop)c);
}
}
}