WPF handle drag and drop as well as left click - c#

I'm having some troubles getting DragDrop.DoDragDrop to work along nicely with a left click event.
My control has several links which can either be dragged around or left clicked to visit.
I currently subscribe to the preview mouse move event which is where i launch the drag and drop event if the left mouse button is pressed.
In another call back i handle the mouse left button down and up event to check for a click. Is there anyway to check if DragDrop actually had a drag drop event take place?

See this link drag drop in wpf explained end to end and scroll down a little to the section "Detecting Drag & Drop"
Code inserted here encase the blog goes missing...
From [http://msdn2.microsoft.com/en-us/library/aa289508(vs.71).aspx] Here is the sequence of events in a typical drag-and-drop operation:
Dragging is initiated by calling the DoDragDrop method for the source control.
The DoDragDrop method takes two parameters:
data, specifying the data to pass allowedEffects, specifying which operations (copying and/or moving) are allowed
A new DataObject object is automatically created.
This in turn raises the GiveFeedback event. In most cases you do not need to worry about the GiveFeedback event, but if you wanted to display a custom mouse pointer during the drag, this is where you would add your code.
Any control with its AllowDrop property set to True is a potential drop target. The AllowDrop property can be set in the Properties window at design time, or programmatically in the Form_Load event.
As the mouse passes over each control, the DragEnter event for that control is raised. The GetDataPresent method is used to make sure that the format of the data is appropriate to the target control, and the Effect property is used to display the appropriate mouse pointer.
If the user releases the mouse button over a valid drop target, the DragDrop event is raised. Code in the DragDrop event handler extracts the data from the DataObject object and displays it in the target control.
Detecting Drag & Drop
Before the DoDragDrop is called, we must detect a mouse Drag operation on the source... A mouse drag is usually a MouseLeftButtonDown + a MouseMove (before MouseLeftButton goes up).
So, our drag & drop source control needs to subscribe to these two events:
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove);
}
To prevent from starting a false drag & drop operation where the user accidentally drags, you can use
SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance
One way to do this is on MouseLeftButtonDown, record the starting position and onMouseMove check if the mouse has moved far enough..
void DragSource_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 DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
Its a Drag .. now what?
The data! You need to find out what is under the mouse when dragging.
I will omit take the easy way out and assume that whoever is triggering the MouseMove is what I want to drag .. so look at MouseEventArgs.OriginalSource.. [or you could do some 2D HitTesting using VisualTreeHelper .. In Part3 of this write up will try to walk you through hit testing the listbox -which is the other common scenario I encounter-.
Once you have the object to drag, you will need to package what you are a sending into a DataObject that describes the data you are passing around.
DataObject is a wrapper to push generic data (identified with extensible formats) into drag/drop.. As long as both the source and destination understand the format, you will be set. As such, DataObject has a couple interesting methods:
SetData ( Type format, object data ) /// format is the "format" of the day you are passing ( e.g. Formats.Text, Formats.Image, etc.. ) you can pass any custom types.
GetDataPresent ( Type format ) /// is what the drop target will use to inquire and extract the data .. if it is a type it can handle, it will call GetData () and handle it ..
Not much interesting stuff here.. In the sample I just hard-coded my data to be of type string... this makes it easier to paste into external containers (for example Word, which you can use to test this part of the write-up). I do have to stress that drag & dropping should be about the data ...
Providing visual feedback during the drag & drop operation..
Before we call DoDragDrop () we have a few 'choices' to make around the feedback we want to provide and the 'scope' of the d&d.
Do we want a custom cursor to display while we are doing the Drag operation ? If we want a cursor, what should it be?
How far do we want to drag? within the app or across windows apps?
Simplest scenario: No custom cursor and we want it to drag across apps:
If you don't want a fancy cursor, you are done!! You can call DoDragDrop directly ...
private void StartDrag(MouseEventArgs e)
{
IsDragging = true;
DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
DragDropEffects de = DragDrop.DoDragDrop(this.DragSource, data, DragDropEffects.Move);
IsDragging = false;
}
Note: this code allows you to drag & drop across processes, it uses the default operating system feedback ( e.g. + for copy)..

there are Drag Over/ Enter / Leave Attached events
you can subscribe methods to these (or one) events on your dragged UIElement and see if the Dragging occurs .

Related

Change the Panning button in Mapsui Control

I'm using Mapsui as a mapping control in a C# application.
By default, panning is initiated by dragging using the left mouse button.
I want to change this to the middle mouse button.
Does anyone know how to do this?
Mapsui has an object called PanMode, you can create an instance as follows, however, I believe it is just an enum for centering the map when panning:
Mapsui.UI.PanMode panMode = new PanMode();
EDIT:
Based on what 'pauldendulk's' answer (thank you for your support) I think I need to do something like this:
First, catch the middle button click and relay it to the mapsui left button method. Unfortuantly MapControlMouseLeftButtonDown() is a private method so this will not work.
MyMapControl.MouseDown += MapControlOnMouseButtonDown;
private void MapControlOnMouseButtonDown(object sender, MouseButtonEventArgs e)
{
if(e.ChangedButton == MouseButton.Middle)
{
Mapsui.UI.Wpf.MapControl.MapControlMouseLeftButtonDown(sender, e);
}
}
Secondly I need to stop the origional left button click from firing.
MyMapControl.MouseLeftButtonDown += null;
Again, this is not correct syntax as it throws an exception (cannot be null).
Does anyone know how to solve these issues?
Mapsui was not designed with this in mind. Perhaps it is possible if you assign an event handler to WPFs mouse down event, set the viewport in there. Also, you need to suppress the regular mouse event. Perhaps this is possible by setting the MouseLeftButtonDown event handler to null.
PanMode is not relevant. It is meant to limit the area where users can pan/zoom.

ListView click event not triggered when clicking on empty areas

I have a ListView control on my form. I have set its display mode to LargeImageList. I need to handle the items inside this control. So I have written code for its click event. But I see now that this event is not triggered when I click in an empty area inside it.
How can I make my ListView aware of the clicks on its area regardless.
To capture mouse clicks on the "white space" around the ListView items, you will need to use the MouseDown/MouseUp events.
This will also capture clicks to the items as well.
I've used the Global Mouse Hook for similar issues. You can use it to detect Mouse Clicks anywhere on the screen, then just check the click was within the listview control bounds.
Grab the code from Global Mouse Key Hook
IKeyboardMouseEvents m_GolbalHook = Hook.GlobalEvents();
m_GolbalHook.MouseClick += m_GolbalHook_MouseClick;
private void m_GolbalHook_MouseClick(object sender, MouseEventArgs e)
{
if (listView.Bounds.Contains(e.Location)) && (e.Button == System.Windows.Forms.MouseButtons.Left))
{
//Do Stuff
}
}

Drag and Drop Windows Form

private void calendarPlanner1_ItemClick(object sender, WeekPlannerItemEventArgs e)
{
DoDragDrop(calendarPlanner1.SelectedItem, DragDropEffects.Move);
}
private void calendarPlanner1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void calendarPlanner1_DragDrop(object sender, DragEventArgs e)
{
SRRepair repairDrag = new SRRepair();
var rows = calendarPlanner1.Rows;
var row = rows.ElementAt(calendarPlanner1.SelectedRowIndex);
row.Items.Add((WeekPlannerItem)e.Data.GetData(typeof(WeekPlannerItem)));
repairDrag = assignedRepairsList[assignedItemCollection.IndexOf(calendarPlanner1.SelectedItem)];
repairDrag.AssignedToUser = engineerList[calendarPlanner1.SelectedRowIndex];
repairDrag.Update();
}
The code above is what I have so far for drag and drop operation. It works ok until the third (drag drop) method. Basically what im trying to achieve is to drag an item between the names. I could get the index where I drag the item from using 'calendar.SelectedRowIndex' but the problem is getting the index of the destination or where i want to drag it to. Allows me to drag items but when i let go of the left button in the mouse it goes back where it came from. The calendar is an open source and i found it from codeproject and i am using and modifying it to add it in an existing desktop application.
Is there anyway or an event I could use to get the position of the mouse as soon I release the left button in the mouse?
I think, beside the drag and drop operation, you need to track your mouse move(or mouse enter) too, for getting the index of one element, drag'n'drop itself would not help(it get's the very first point), just track and identify the target by mouse move.
or simply, add mouse enter event for the object(controls) you want targeted by the drop, choose the target position via mouse enter, and complete the oration via drop, and it's better to remove mouse enter event when the drop operation completed, and add it by drop enter again.
hope I got your question true
I'm pretty sure in a desktop application you can get the mouse position without having to rely on passed mouseevent arguments.
control.mouseposition - http://msdn.microsoft.com/en-us/library/system.windows.forms.control.mouseposition.aspx
you should just be able to get the 'point' value through 'control'.mouseposition. where the control is likely the form you're working with.
Edit
As stated in the reference, the control.mouseposition method is identical to this.cursor.position.
cursor.position - http://msdn.microsoft.com/en-us/library/system.windows.forms.cursor.position.aspx
Additionally, you're going to want to locate the controls (plural), which are located at (or have ClientRectangle bounds that encompass) the cursor location point you capture..
control.GetChildAtPoint(Point).. which you may have to do recursively..
GetChildAtPoint - http://msdn.microsoft.com/en-us/library/ms158404.aspx

C# Event-Handling

I am working on a C# piano. I have already built the music keyboard and the staff. Everytime a user presses a key, it is displayed on the staff in its relevant position.
The music note displayed on the staff is stored in an array of pictureboxes, as shown below.
public void addPictureBox(int x, int y, Image image)
{
picBox[cnt] = new PictureBox();
picBox[cnt].Image = image;
picBox[cnt].Location = new Point(x, y);
picBox[cnt].BackColor = Color.Transparent;
panel3.Controls.Add(picBox[cnt]);
picBox[cnt].BringToFront();
picBox[cnt].MouseDown += new MouseEventHandler(Pic_MouseDown);
picBox[cnt].MouseUp += new MouseEventHandler(Pic_MouseUp);
cnt++;
}
The Pic_MouseDown and Pic_MouseUp events allow the user to play the note by clicking on it from the staff.
What I want to do now is to create an event on picBox[cnt] for dragging. However, picBox[cnt].MouseDown and picBox[cnt].MouseUp have already been registered to Pic_MouseDown and Pic_MouseUp event handlers.
How can I do an event to handle dragging since MouseDown and MouseUp have already been registered to other event handlers?
Thanks :)
The great thing about event handlers is that you can have as many attached handlers as you want. The += (operator overload) means you are attaching a new event handler to the existing handlers. You can add as many event handlers as you desire.
Event Handler Overview
If you create a isDragging boolean instance field, which you set to true in the mouse down and false in mouse up, then you can use the mouse move event to detect whether the object should be moved or not.
You'll need to use a combination of MouseDown, MouseMove and MouseUp events. In MouseDown, you do little more than set a flag to indicate that the mouse was pressed, and record where it was pressed. In MouseMove, you check to see if the button is still down and the cursor has moved further than SystemInformation.DragSize, which indicates that the user is dragging rather than clicking, and start the drag operation if needed. In MouseUp, you either complete the drag or perform the click action.
I believe the conventional wisdom for Drag-n-drop is
Call DoDragDrop in the (source control's) MouseDown handler
set AllowDrop = true in the targe control's AllowDrop property
There's a whole series of drag events to fine tune behavior
One does not seem to have to worry about any latent MouseUp or MouseClick events. I'm assuming this is because the physical actions of button press and release are done over different controls and/or the mouse physically moved "far enough".

SIngle and Double click events not working as expected in canvas

I have a Canvas which is present in a UserControl, I have attached the DoubleClick event to this UserControl like this
mainWindow.CanvasLayout.MouseDoubleClick +=
new MouseButtonEventHandler(CanvasLayout_MouseDoubleClick);
I am using this event handler to achieve full screen functionality.
Now, Canvas can have various controls placed inside it. Drag and drop functionality is implemented for these controls similar to this codeproject article.
Basically, I handle these events for a control -
this._dragSource.PreviewMouseLeftButtonDown +=
new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
this._dragSource.PreviewMouseMove +=
new System.Windows.Input.MouseEventHandler(DragSource_PreviewMouseMove);
this._dragSource.PreviewMouseLeftButtonUp +=
new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonUp);
Now, when user DoubleClicks on a control(present in canvas) both DoubleClick(Full Screen) and single Click(drag & drop) operations are performed, i.e. if user double clicks on control and change its mouse position quickly, control position is changed(its dragged and dropped to new position).
Is there any way I can prevent drag and drop operation when user double clicks on a control?
Got it, Instead of handling MouseDoubleClick event, I used PreviewMouseLeftButtonDown -
mainWindow.CanvasLayout.PreviewMouseLeftButtonDown
+= new MouseButtonEventHandler(CanvasLayout_PreviewMouseLeftButtonDown);
and
void CanvasLayout_PreviewMouseLeftButtonDown(object s, MouseButtonEventArgs e)
{
if (e.ClickCount > 1)
{
// Do double-click code
// Code for FullScreen
e.Handled = true;
}
}
What you need to do is, on mouse up start a timer the time should be retrieved from SystemInformation.DoubleClickTime, and do the click action on the timer tick (only if you didn't detect the double click in the mean time, obviously).
Likewise use SystemInformation.DragSize to prevent accidental drag&drop.
Note, the SystemInformation class is part of WinForms so you need to add a reference to System.Windows.Forms, this class will work just fine in WPF apps.

Categories