Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DragDrop.DoDragDrop not returning when doing a drop operation into Excel

My application has a simple feature where it hooks up to Excel and will do drag drop operations between them. Specifically, I am just taking some text values from my application, dragging them into Excel, and dropping them.

This works 90% of the time, but strangely at certain times, my application just freezes. I attach the debugger and pause the execution and it gets stuck at DragDrop.DoDragDrop - this function never returns and my application will forever hang.

Is there a way to ensure the DoDragDrop can return? Or some sort of timeout? This happens only sometimes when I drop the data into Excel, so for what I know, the drop is being completed and the function should return within my application.

Here's the code I use:

DragDrop.DoDragDrop(sender as DependencyObject, draggable.GetDragDropString(), DragDropEffects.Copy);

GetDragDropString() is just a function that returns the string of data to drop in Excel. sender is just the UI component that I'm dragging. Like a grid, or edit box, text box, etc. Could be any of those.

Thanks for any help!

EDIT: Since there's an issue with DragDrop.DoDragDrop returning in certain cases, perhaps someone can help with writing a proper timeout? I've tried starting a new Thread and having it timeout, which works in simple cases and when the work within the thread doesn't require UI resources. However, when I call DoDragDrop in a new thread with a timeout, it will throw an exception saying the thread cannot access the object because a different thread owns it. So I need to call this function within the same thread. So essentially I need a timeout on the UI thread when this function fails to return in a certain amount of time.

like image 850
ryrich Avatar asked Feb 02 '16 15:02

ryrich


1 Answers

I think the following should do the job but I will break it down as I go along

public class DragDropTimer
{
    private delegate void DoDragDropDelegate();
    private System.Timers.Timer dragTimer;
    private readonly int interval = 3000;

    public DragDropTimer()
    {
        dragTimer = new System.Timers.Timer();
        dragTimer.Interval = interval;
        dragTimer.Elapsed += new ElapsedEventHandler(DragDropTimerElapsed);
        dragTimer.Enabled = false;
        dragTimer.AutoReset = false;
    }

    void DragDropTimerElapsed(object sender, ElapsedEventArgs e)
    {
        Initiate();
    }

    public void Initiate()
    {
        // Stops UI from freezing, call action async.
        DoDragDropDelegate doDragDrop = new DoDragDropDelegate(DragDropAction);

        // No async callback or object required
        doDragDrop.BeginInvoke(null, null);
    }

    private void DragDropAction()
    {
        dragTimer.Enabled = false; 

        // Do your work here. or do work before and disable your timer upto you.

    }

}

So we have a basic class DragDropTimer. We set the interval we want on constructor, you may want to change that if you wish and we call the DragDropTimerElapsed on timer elapsed.

Initiate is the function you need to start the drag, its creates a simple delegate and we ask it to execute the DragAction steps, this is where you do all your work and the timer gets disabled. You may choose to disable the timer only if you DragDrop succeeds. If the timer elapses we call Initiate again to start all over again.

like image 197
Samer Tufail Avatar answered Sep 20 '22 17:09

Samer Tufail