Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF handle drag and drop as well as left click

Tags:

c#

wpf

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?

like image 377
bgura Avatar asked Dec 04 '22 01:12

bgura


1 Answers

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)..

like image 129
Paul Zahra Avatar answered Dec 25 '22 17:12

Paul Zahra