1

I have a range of drag drop activities all working fine on my quiz. (From answering questions to selecting an avatar)

There is a lot of repetition of the code and I am thinking it would be better implemented from a class and call the method each time that I use a drag drop.

Firstly can this be done? and secondly would I need a new method for the dragging and dropping?

Any thoughts or rough ideas would be great.

private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
{
pictureBox2.DoDragDrop(pictureBox2.Image, DragDropEffects.Copy);
}

private void panel1_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
}

private void panel1_DragDrop(object sender, DragEventArgs e)
{
    //Set background image of panel to selected avatar'
    panel1.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
}
9
  • Is a Web Forms or Windows Win Forms application? Commented Oct 22, 2015 at 15:56
  • Could you give some code as example that is getting repeated and that you'll like to refactor ? Commented Oct 22, 2015 at 16:11
  • I added the code. I was thinking a class called drag and a the methods inside for the drag and drop Commented Oct 22, 2015 at 17:03
  • My usual way for such a situation is to create a Controller class that handles all the code. The controls register (and maybe unregister) to it, maybe with a few extra params, depending on what you need in the code. The the controller hooks up the necessary events upon registration and you're good. I have done this for several cases including resize- and move controllers, not d&d yet but it should work just as well... Commented Oct 22, 2015 at 17:03
  • 1
    Very unclear exactly what code is getting repeated. Random guess: write one event handler for all pictureboxes. That's why it has a sender argument. Commented Oct 22, 2015 at 17:09

1 Answer 1

2

If all you want is to avoid duplicating the same events of several controls you should use common events:

private void commonPBox_MouseDown(object sender, MouseEventArgs e)
{
    PictureBox PB = sender as PictureBox;
    if (PB == null) return; //or throw an exception

    PB.DoDragDrop(PB.Image, DragDropEffects.Copy);
}

private void commonPanel_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
}

private void commonPanel_DragDrop(object sender, DragEventArgs e)
{
    Panel Pan = sender as Panel;
    if (Pan == null) return; //or throw an exception

    //Set background image of panel to selected avatar
    Pan.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
}

Select the respective controls and enter the event names into the respective event name slots in the event pane of the properties tab.

Note how I cast the sender param of the event to get a types reference to the control that triggers the event. If the cast goes wrong the reference is set to null.

If you want more control and more flexibilty you may consider creating a DragAndDropcontroller class..:

static class DnDCtl
{
    static List<Control> Targets = new List<Control>();
    static List<Control> Sources = new List<Control>();

    static public void RegisterSource(Control ctl)
    {
        if (!Sources.Contains(ctl) ) 
        {
            Sources.Add(ctl);
            ctl.MouseDown += ctl_MouseDown;
        }
    }

    static public void UnregisterSource(Control ctl)
    {
        if (Sources.Contains(ctl))
        {
            Sources.Remove(ctl);
        }
    }

    static public void RegisterTarget(Control ctl)
    {
        if (!Targets.Contains(ctl))
        {
            Targets.Add(ctl);
            ctl.DragEnter += ctl_DragEnter;
            ctl.DragDrop += ctl_DragDrop;
            ctl.AllowDrop = true;
        }
    }

    static public void UnregisterTarget(Control ctl)
    {
        if (Targets.Contains(ctl))
        {
            Targets.Remove(ctl);
            ctl.DragEnter -= ctl_DragEnter;
            ctl.DragDrop -= ctl_DragDrop;

        }
    }

    static void ctl_MouseDown(object sender, MouseEventArgs e)
    {
        PictureBox PB = sender as PictureBox;
        if (PB != null) PB.DoDragDrop(PB.Image, DragDropEffects.Copy);

        Panel Pan = sender as Panel;
        if (Pan != null) Pan.DoDragDrop(Pan.BackgroundImage, DragDropEffects.Copy);
    }

    static void ctl_DragEnter(object sender, DragEventArgs e)
    {
        e.Effect = DragDropEffects.Copy;
    }

    static void ctl_DragDrop(object sender, DragEventArgs e)
    {
        Panel Pan = sender as Panel;
        if (Pan != null) Pan.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);

        PictureBox PB = sender as PictureBox;
        if (PB != null) PB.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
    }
}

Notes:

  • I have coded symmetric actions for Panels and PictureBoxes to show how you can handle different controls.
  • I have created but not used Lists of sources and targets. For more complex projects you will find them useful.
  • I have coded both Register and Unregister methods. You can register after some condition and unregister when it no longer applies
  • Such a Controller is good for dynamically alowing or disallowing Drag&Drop for controls, esp. when you create them dynamically.
  • You could also pass around delegates to decouple the dragdrop action from the controller.

Also note that some folks sometimes do prefer to use the MouseMove event over MouseDown esp. as it won't so easily interfere with making a selection.

ListView has a dedicated event instead, ListView.ItemDrag, which obviously should be used when registering!

Sign up to request clarification or add additional context in comments.

11 Comments

Yea I think it is the Controller that I was thinking of, I am going to have to practice this. Thank you for your extremely helpful feedback and explanations.
Sorry I'm a complete noob, how do I accept an answer?
Well, the controller code is a class and if you place it anywhere in your project, except within another class you will be able to use it anywhere. All the methods are static and independent from the outside, relying only on the sender and the e.Data parameters, so yes, it will work across forms. That is If you use the just corrected version of it - I had added the DragDrop event hooks to the source instead of the target! My fault, sorry!
Can you tell me a little about the trouble you have? Note that you need to be sure to register each event only once or else the event gets called more ofteten than once; the register and unreister calls must match!
- If instead you want to allow only one dragdrop action for the source you need to create a reference: static Control source = null; in the controller class, set it in the mouse down: source = sender as Control; and test, unregister and clear it in the dragdrop: if (source != null) { UnregisterSource(source); source = null; } In this case you should remember which was the previously chosen avator so you can re-register it for re-selecting it later.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.