문제

A good morning to you all! Yesterday I ran into a problem while trying to implement a custom DragDrop for my own controls in a WinForms application.

I have a form which can dynamically create instances of two of my own controls. These controls consist of some controls themselves, such as buttons, labels and listboxes/treeviews. The controls serve as a representation for a certain dataset. Now, we all know the class diagrams in VS. There you have these boxes representing classes. You can move the boxes around on the canvas by doing - what I would call - dragging them around, much like you would drag around files. To accomplish this with my own controls I have done the following:

public partial class MyControl: UserControl
{
private Control activeControl;

private void GeneralMouseDown(MouseEventArgs e)
{
    activeControl = this;
    previousLocation = e.Location;
    Cursor = Cursors.Hand;   
}

private void GeneralMouseMove(Control sender, MouseEventArgs e)
{
    if (activeControl == null || activeControl != sender)
        return;
    var location = activeControl.Location;
    location.Offset(e.Location.X - previousLocation.X, e.Location.Y - previousLocation.Y);
    activeControl.Location = location;
}

private void GeneralMouseUp()
{
    activeControl = null;
    Cursor = Cursors.Default;
}
}

The controls on my control which I want to "grab" for dragging MyControl have their MouseDown-, MouseMove- and MouseUp-events pointing to these three methods. As a result I can move my control about on the form freely, just as I want to.

Here comes the tricky bit: The datasets I have controls for can be in hierarchical dependencies, which means, one control represents detailling of a component of the other, which is why my controls have Listboxes or TreeViews. To establish such a hierarchical dependency I would very much like to DragDrop the lower-order-control on the listbox of my higher-order-control, causing data to be transfered.

I know how to set up my DragEnter and DragDrop methods for the listbox, as I have done so previously with files. Just for completeness:

private void lst_MyControl_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(MyControl)))
        e.Effect = DragDropEffects.Move;
    else e.Effect = DragDropEffects.None;
}

Here's the problem: As I am moving my control about (which gets repainted at every position, giving a very much wanted effect!), when I "drag" it over the target-listbox, the DragEnter-event does not get fired. I thought I could work around this problem by telling Windows "Hey, I'm, Dragging'n'Dropping here!", thus adding to my GeneralMouseDown-method:

this.DoDragDrop(this, DragDropEffects.Move);

This, on the one hand, gets the DragEnter-event to fire => Yeah! On the other hand is the moving-around-part only working after I release the mouse, causing the control to hang on the mousepointer forever => Anti-Yeah!

Here's the question: Is there a way, to have both actions at the same time? So that I can move my control around, seing it at every position as I do now and fire the DragEnter-event when I get to that area of the other control?

도움이 되었습니까?

해결책 2

After a bit of fiddeling I did it. I switched the level on which the dragging is handled.

First I need just the MouseDown-event

public Point GrabPoint;

private void GeneralMouseDown(MouseEventArgs e)
{
    GrabPoint = e.Location;
    this.DoDragDrop(this, DragDropEffects.Move);
}

I set the point where I grab the control and initiate a DragDrop. On my form I handle all the dragging:

private void frmMain_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(MyControl1)) || e.Data.GetDataPresent(typeof(MyControl2)) || e.Data.GetDataPresent(typeof(MyControl3)))
        e.Effect = DragDropEffects.Move;
    else e.Effect = DragDropEffects.None;
}

private void frmMain_DragOver(object sender, DragEventArgs e)
{
    Point DragTarget = new Point(e.X, e.Y);
    Point GrabPoint = new Point(0, 0);
    if (e.Data.GetDataPresent(typeof(MyControl1)))
        GrabPoint = ((MyControl1)e.Data.GetData(typeof(MyControl1))).GrabPoint;
    else if (e.Data.GetDataPresent(typeof(MyControl2)))
        GrabPoint = ((MyControl2)e.Data.GetData(typeof(MyControl2))).GrabPoint;
    else if (e.Data.GetDataPresent(typeof(MyControl3)))
        GrabPoint = ((MyControl3)e.Data.GetData(typeof(MyControl3))).GrabPoint;
    DragTarget.X -= GrabPoint.X;
    DragTarget.Y -= GrabPoint.Y;
    DragTarget = this.PointToClient(DragTarget);
    if (e.Data.GetDataPresent(typeof(MyControl1)))
        ((MyControl1)e.Data.GetData(typeof(MyControl1))).Location = DragTarget;
    else if (e.Data.GetDataPresent(typeof(MyControl2)))
        ((MyControl2)e.Data.GetData(typeof(MyControl2))).Location = DragTarget;
    else if (e.Data.GetDataPresent(typeof(MyControl3)))
        ((MyControl3)e.Data.GetData(typeof(MyControl3))).Location = DragTarget;
}

At the moment I don't need the DragDrop-event, since nothing should happen when any control is dropped on the form. This way I always paint my control while it is being dragged => Yeah!

The next part is easy: Since I am really dragging the control, this bit of code does DragDrop-handling on my listbox Edit: ListView:

private void lst_SubControls_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(MyControl2)))
        e.Effect = DragDropEffects.Move;
    else e.Effect = DragDropEffects.None;
}

private void lst_SubControls_DragDrop(object sender, DragEventArgs e)
{
    lst_SubControls.Items.Add(((MyControl2)e.Data.GetData(typeof(MyControl2))).SpecificDrive);
    ((MyControl2)e.Data.GetData(typeof(MyControl2))).DeleteThisControl();
}

This results in an entry added to the list and deletion of the dragged control. At this point there could be a check, wether the ctrl-key is pressed to copy the contents and not to delete the control.

다른 팁

Moving your Control around interferes with the automatic DragDrop handling.

I'd recommend to staying with the normal DragDrop procedures, that is leaving all visuals to the system: It will display a cursor that indicates when a valid target is entered, then change to one that indicates the operation.

You need just 3 lines, no hassle and the user won't seen bulky controls moving around.

Here is a version where I drag a PictureBox onto a ListBox:

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

    private void listBox1_DragDrop(object sender, DragEventArgs e)
    {
        listBox1.Items.Add( e.Data.GetData(DataFormats.Text));
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Left) 
            pictureBox1.DoDragDrop(pictureBox1.ImageLocation, DragDropEffects.Copy);
    }

Obviously you will set up and receive your data in your own ways..

Edit: Now, if on the other hand you need to move controls around to rearrange them, maybe you should give up on Drag&Drop to handle the additional data transfers and code this portion on your own as well. You could use the MouseEnter event of a receiving control..

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top