How to get Select-Drag (drag and drop) working

Topics: User Forum
Mar 19, 2013 at 12:31 PM
Hi, I'm using ScintillaNET 2.5.2 and I'm struggling to get the select-drag functionality going.

This is the feature where the user selects a text block, then click-drags that selection to another location within the same document.

looking at the drag and drop example in http://scintillanet.codeplex.com/workitem/25148 the control appears to just work out of box, although that was based on 2.2.0.

Also looking at that Issue, it was fixed in 2.5.1 which is only one release ago.

I've currently got a test project, with nothing but a vanilla ScintillaNet control on a form and no joy.

I must be doing something dumb, but I just can't see it
Mar 19, 2013 at 11:17 PM
Just to check, do you have AllowDrop set to true?
Mar 25, 2013 at 4:39 PM
thanks for getting back so quick,

yes, tried set to true and false..

when it's set to true, I can see the events firing - in the example these are not handled - should I be handling them?
Mar 26, 2013 at 11:24 PM
Let me run some tests with my own project, and I'll get back to you.
Mar 26, 2013 at 11:58 PM
Edited Mar 27, 2013 at 12:50 AM
Let me run some tests with my own project, and I'll get back to you.

Sorry, accidental repost.
Mar 27, 2013 at 12:41 AM
Edited Mar 27, 2013 at 12:41 AM
Ok, I have a solution.

First, create an event handler for the DragEnter event. Then, use this code:
if (e.Data.GetDataPresent(DataFormats.Text))
    e.Effect = DragDropEffects.Copy;
else
    e.Effect = DragDropEffects.None;
Then, create another event handler for the DragDrop event, and use this ugly hack to paste in the text. It only copies right now (as opposed to properly cut/pasting), but you could probably use the Scintilla Selection property to delete the selection before you paste.
Editor.InsertText(Editor.PositionFromPoint(
    Editor.PointToClient(new Point(e.X, e.Y)).X, 
    Editor.PointToClient(new Point(e.X, e.Y)).Y), 
    e.Data.GetData(DataFormats.Text).ToString());
Change Editor to the name of your Scintilla object.
Apr 2, 2013 at 3:43 PM
Thanks for looking into that,

I've had a play, and although I think it might work, It all feels very 'hacky' to me.

Firstly, there's no drop caret - when you move the mouse during the drag (e.g. in notepad++) you get an extra little caret to show you where the text would drop if you let go and using this approach I'd need to overdraw my own to achieve the same effect,

Secondly, recalculating the paste position for a move is a headache, because as soon as you delete the currently selected text the paste position moves, so the new position would have to re-calculated.

All this is very doable, but it's a bunch of work, and it just feels like we're re-inventing the wheel.


I had a look in the Scintilla.Net source and in Scintilla.cs tried commenting out this section: (line 1105)
            // The Scintilla native control kindly registers itself as a target of OLE drag-and-drop operations,
            // however, that prevents us from doing the same. Unregistering it prior to calling base.OnHandleCreated
            // will re-register us according to the AllowDrop property and we'll get support for the Drag events.
            if (IsHandleCreated)
                NativeMethods.RevokeDragDrop(Handle);
This was interesting - with this removed none of the scintilla.net drag drop events fired at all, which is as the comment suggests.
However now that the native control is servicing drag and drop, click-drag now magically works!
Also, so does dragging text to and from other controls within my app, and external apps including np++ and word.

So on the face of it this solves my problem, except it doesn't, because I now have a non-standard version of scintilla.net :(
Could calling 'RevokeDragDrop' be made optional?

It's also set me thinking - why does scintilla.net even need it's own drag and drop interface if the underlying scintilla control is already servicing the events?
Developer
Apr 2, 2013 at 7:29 PM
The reason we have to handle the drag drop ourselves is so that we can allow users to add custom drag or drop handling, perhaps inserting the raw data of an image that is dropped onto it, or inserting the text that is in a file that's dropped on it. With drag drop operations being handled by the native side, we can't hook those events to allow that support, so we have to disable the handling done by the native side and instead handle it ourselves.
Apr 3, 2013 at 8:32 AM
That's a fair point, although it does rather saddle users who just want the basics with a bunch of work :(

What about instead of calling RevokeDragDrop, we call DragAcceptFiles() instead?
[DllImport("shell32.dll", CharSet=CharSet.Ansi, ExactSpelling=true)]
public static extern void DragAcceptFiles(HandleRef hWnd, bool fAccept);
            if (IsHandleCreated)
                NativeMethods.DragAcceptFiles(new HandleRef( this, this.Handle ),true);
and then handling the WM_DROPFILES message in WndProc.


This would allow Scintilla to retain it's native drag and drop support, while giving Scintilla.NET the hook to handle files that are dropped on it.

I think that both your examples would work with this.

What do you think?
Developer
Apr 4, 2013 at 1:40 PM
That would work for the examples I gave, but there are many others that wouldn't (say dragging a UI control onto it). Perhaps a better solution would be to pass the drag/drop events we receive through to Scintilla, provided it wasn't handled on the managed side, allowing it to handle what it wants, while also allowing users to do custom handling if they want?
Apr 4, 2013 at 4:06 PM
Sounds good, even better if this 'pass-through' can be on by default :)

Sadly, there seem to be a bunch of feature requests with scintilla for better drag and drop integration between the control and it's parent, none of which have been implemented as far as I can see.
Here's one which I think is the reverse of what your suggesting...
http://sourceforge.net/p/scintilla/feature-requests/890/

It's basically asking for notifications to be sent to the parent when drop events occur that Scintilla won't handle, although the discussion at the end sounds more like the other way around (what you said in other words). Again, there's no indication that this was ever implemented.

It did mention looking at SciTE to see what it does - apparently it uses the same system as Notepad++ and just handles the WM_DROPFILES message.

If there's a way of getting your suggestion of forwarding the events back to scintilla then I'd say go for that, but If that doesn't work out then perhaps the behavior could be made optional; e.g.
  1. Basic (default) setup: native+wm_dropfiles (default scintilla native support, can add handler for event fired on WM_DROPFILES if desired. User has to do nothing to get basic support)
  2. Advanced setup: managed only ( what we have now, with the call to revokeDragDrop and working managed events. User has complete control, but has to implement everything )
I've no idea about how to go about passing the events back to scintilla, but I could certainly create a patch for the options above if that helps? - I'll even write the user docs too if you like :)