drag into another process
I am trying to drag an item out into the explorer.
The item should result in a file download, so I've used an example I've found on the web to download the file using aCustomDataObject
that calls an event when he actually needs the stream, and then my application does the heavy lifting and performs the download.
It's been working just fine in a similar Clipboard operation.
The actual file download also causes some UI changes in my application. Mostly a "working" icon changing on the form, and also a popup balloon in case of an error.
In the clipboard operation I just used InvokeRequired
and BeginInvoke
when needed, to make sure those UI changes happen on the main thread. In the drag operation, the UI thread is waiting for the return from the DoDragDrop
, while the event being raised by the CustomDataObject
is being called on a different thread. When I try to call BeginInvoke
or Invoke
the UI thread is still waiting, and I can't finish the drop.
Is there some sample, or a recommended best practice, on how to allow cross-application drag n drop, while accessing the UI of the source application?
UPDATE
here is the original CodeProject article with the DataObjectEx
I modified for my own use. I just changed the GetFileContents
method to call a virtual method which returns a Stream
containing the file data, inherited from the class, and overridden that virtual method to get the file from the web.
The problem arose when I wanted to change stuff in the UI, while getting the file. As I said earlier - the main UI thread is still "stuck" 开发者_运维知识库at the DoDragDrop
method call, so I can't invoke it on time to do the UI changes needed by the worker thread before and after downloading the file.
If this is a standard WinForms application, then all you really need to do in your application is to add event handlers in your Form for DragEnter and DragDrop.
Inside of DragEnter, you'll want to check the type of object to make sure it is a filename:
private void MyForm_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files != null)
{
// Do additional checks here if needed, like check extensions
e.Effect = DragDropEffects.Copy;
return;
}
}
e.Effect = DragDropEffects.None;
}
Then in your DragDrop handler, I would simply store off the file names, and then activate a timer. This allows DragDrop to return immediately, so that the other application (in your example, Windows Explorer) does not hang while you do any processing on the file which may take time. The Drag Source will not return until DragDrop finishes.
private void MyForm_DragDrop(object sender, DragEventArgs e)
{
string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files != null)
{
_filesToProcess.Text = files[0]; // Assuming this is declared at the Form level
// Schedule a timer to fire in a few miliseconds as a simple asynchronous method
_DragDropTimer.Interval = 50;
_DragDropTimer.Enabled = true;
_DragDropTimer.Start();
Activate(); // Activates the form and gives it focus
}
}
I had the same issue and found that System.Windows.Forms.Control.DoDragDrop ignored my Form's implementation of COM's IAsyncOperation, instead using the WinForm's DataObject implementation of IDataObject internally. Unfortunately WinForm's DataObject class does not implement IAsyncOperation.
So I used this project's VirtualFileDataObject implementation of IAsyncOperation, IDataObject, calling VirtualFileDataObject.DoDragDrop instead of Control.DoDragDrop. I set a VirtualFileDataObject.FileDescriptor.StreamContents to a delegate where I Invoke onto the UI thread to report progress while downloading the file.
精彩评论