重新排列多个流式布局中的自定义控件

huangapple go评论57阅读模式
英文:

Reorder customcontrol inside multiple flowLayoutPanel

问题

以下是您要翻译的内容:

"I am trying to make an app to generate a tierlist.
Each tier consists of a flowlayout and you basically move the controls around by drag-and-drop.
重新排列多个流式布局中的自定义控件
The problem is that even though I can move the elements with drag-and-drop, the element is always added to the end of the list, and not where you drop the pointer.
Is there any way to maintain order during drag-and-drop?

These are the methods that are in the flowlayout

private void flow_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Move;
}
private void flow_DragDrop(object sender, DragEventArgs e)
{
    ((UserControl1)e.Data.GetData(typeof(UserControl1))).Parent = (Panel)sender;
}

While these are from the usercontrol:

private void UserControl1_MouseDown(object sender, MouseEventArgs e)
{
    this.DoDragDrop(this, DragDropEffects.Move);
}
```"

[1]: https://i.stack.imgur.com/dCMoK.png

<details>
<summary>英文:</summary>

I am trying to make an app to generate a tierlist.
Each tier consists of a flowlayout and you basically move the controls around by drag-and-drop.
[![enter image description here][1]][1]
The problem is that even though I can move the elements with drag-and-drop, the element is always added to the end of the list, and not where you drop the pointer.
Is there any way to maintain order during drag-and-drop?

These are the methods that are in the flowlayout

private void flow_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void flow_DragDrop(object sender, DragEventArgs e)
{
((UserControl1)e.Data.GetData(typeof(UserControl1))).Parent = (Panel)sender;
}


While these are from the usercontrol:

private void UserControl1_MouseDown(object sender, MouseEventArgs e)
{
this.DoDragDrop(this, DragDropEffects.Move);
}



  [1]: https://i.stack.imgur.com/dCMoK.png

</details>


# 答案1
**得分**: 1

你可以选择 [这个][1] 解决方案,将被拖动的控件重新排列到 `FlowLayoutPanel` 控件中。这部分利用了 [`Control.ControlCollection.GetChildIndex`][2] 和 [`Control.ControlCollection.SetChildIndex`][3] 方法。

假设你有一个名为 `DragDropControl` 的自定义控件或用户控件:

```c#
public class DragDropControl : Control
{
    public DragDropControl()
    {
        AllowDrop = true;
        BackColor = Color.LightSteelBlue;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        TextRenderer.DrawText(e.Graphics, Text, Font, 
            ClientRectangle, Color.Black, 
            TextFormatFlags.HorizontalCenter |
            TextFormatFlags.VerticalCenter);
    }       
}

<sup>注意: 从图片中看到的情况,只需使用一个简单的 Label 控件代替即可。</sup>

让我们创建一个自定义的 FlowLayoutPanel 并封装所有必需的功能,以便不必在每个 FLP 实现中重复这些功能。

public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
    public DragDropFlowLayoutPanel()
    {
        AllowDrop = true;            
    }

    [DefaultValue(true)]
    public override bool AllowDrop 
    { 
        get =&gt; base.AllowDrop; 
        set =&gt; base.AllowDrop = value; 
    }

自定义的 FLP 实现了其子控件的鼠标、拖放事件。

    protected override void OnControlAdded(ControlEventArgs e)
    {
        base.OnControlAdded(e);

        if (e.Control is DragDropControl)
        {
            e.Control.DragOver += OnControlDragOver;
            e.Control.DragDrop += OnControlDragDrop;
            e.Control.MouseDown += OnControlMouseDown;
        }
    }

    protected override void OnControlRemoved(ControlEventArgs e)
    {
        base.OnControlRemoved(e);

        e.Control.DragOver -= OnControlDragOver;
        e.Control.DragDrop -= OnControlDragDrop;
        e.Control.MouseDown -= OnControlMouseDown;
    }

处理子控件的 MouseDown 事件以进行拖动:

    private void OnControlMouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            var control = sender as DragDropControl;
            DoDragDrop(control, DragDropEffects.Move);
        }
    }

处理 FLP 及其子控件的 DragEnterDragOver 方法和事件,以设置拖放效果。

    protected override void OnDragEnter(DragEventArgs e)
    {
        base.OnDragEnter(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        base.OnDragOver(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    private void OnControlDragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));

            if (GetChildAtPoint(p) == ddc)
                e.Effect = DragDropEffects.None;
            else
                e.Effect = DragDropEffects.Move;
        }
    }

最后,从 DragDrop 方法重写和事件中调用 DropControl 方法,并传递 DragEventArgs 参数。

    protected override void OnDragDrop(DragEventArgs e)
    {
        base.OnDragDrop(e);
        DropControl(e);
    }

    private void OnControlDragDrop(object sender, DragEventArgs e)
    {
        DropControl(e);
    }

被拖动的控件将占据鼠标位置下的控件的索引,如果没有,则将其插入到 Controls 集合的末尾。

    private void DropControl(DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));
            var child = GetChildAtPoint(p);
            var index = child == null
                ? Controls.Count
                : Controls.GetChildIndex(child);

            ddc.Parent = this;
            Controls.SetChildIndex(ddc, index);
        }
    }
}

将所有这些内容放在一起。

public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
    public DragDropFlowLayoutPanel()
    {
        AllowDrop = true;            
    }

    [DefaultValue(true)]
    public override bool AllowDrop 
    {
        get =&gt; base.AllowDrop; 
        set =&gt; base.AllowDrop = value; 
    }

    protected override void OnControlAdded(ControlEventArgs e)
    {
        base.OnControlAdded(e);

        if (e.Control is DragDropControl)
        {
            e.Control.DragOver += OnControlDragOver;
            e.Control.DragDrop += OnControlDragDrop;
            e.Control.MouseDown += OnControlMouseDown;
        }
    }

    protected override void OnControlRemoved(ControlEventArgs e)
    {
        base.OnControlRemoved(e);

        e.Control.DragOver -= OnControlDragOver;
        e.Control.DragDrop -= OnControlDragDrop;
        e.Control.MouseDown -= OnControlMouseDown;
    }

    protected override void OnDragEnter(DragEventArgs e)
    {
        base.OnDragEnter(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        base.OnDragOver(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragDrop(DragEventArgs e)
    {
        base.OnDragDrop(e);
        DropControl(e);
    }

    private void OnControlDragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));

            if (GetChildAtPoint(p) == ddc)
                e.Effect = DragDropEffects.None;
            else
                e.Effect = DragDropEffects.Move;
        }
    }

    private void OnControlDragDrop(object sender, DragEventArgs e)
    {
        DropControl(e);
    }

    private void OnControlMouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            var control = sender as DragDropControl;
            DoDragDrop(control, DragDropEffects.Move);
        }
    }

    private void DropControl(Drag

<details>
<summary>英文:</summary>

You can opt [this][1] solution to reorder the dropped controls into a  `FlowLayoutPanel` control. The part that utilizes the [`Control.ControlCollection.GetChildIndex`][2] and [`Control.ControlCollection.SetChildIndex`][3] methods.

Let&#39;s say you have a custom Control or UserControl named `DragDropControl`:

```c#
public class DragDropControl : Control
{
    public DragDropControl()
    {
        AllowDrop = true;
        BackColor = Color.LightSteelBlue;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        TextRenderer.DrawText(e.Graphics, Text, Font, 
            ClientRectangle, Color.Black, 
            TextFormatFlags.HorizontalCenter |
            TextFormatFlags.VerticalCenter);
    }       
}

<sup>Note: From what I see in the images, just use a simple Label control instead.</sup>

Let's create a custom FlowLayoutPanel and encapsulate all the required functionalities to not repeat that in your implementation for each FLP.

public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
    public DragDropFlowLayoutPanel()
    {
        AllowDrop = true;            
    }

    [DefaultValue(true)]
    public override bool AllowDrop 
    { 
        get =&gt; base.AllowDrop; 
        set =&gt; base.AllowDrop = value; 
    }

The custom FLP implements the mouse, drag and drop events of its children as well.

    protected override void OnControlAdded(ControlEventArgs e)
    {
        base.OnControlAdded(e);

        if (e.Control is DragDropControl)
        {
            e.Control.DragOver += OnControlDragOver;
            e.Control.DragDrop += OnControlDragDrop;
            e.Control.MouseDown += OnControlMouseDown;
        }
    }

    protected override void OnControlRemoved(ControlEventArgs e)
    {
        base.OnControlRemoved(e);

        e.Control.DragOver -= OnControlDragOver;
        e.Control.DragDrop -= OnControlDragDrop;
        e.Control.MouseDown -= OnControlMouseDown;
    }

Handle the child controls MouseDown event to do the drag:

    private void OnControlMouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            var control = sender as DragDropControl;
            DoDragDrop(control, DragDropEffects.Move);
        }
    }

Handle the DragEnter and DragOver methods and events of the FLP and its children to set the drop effect.

    protected override void OnDragEnter(DragEventArgs e)
    {
        base.OnDragEnter(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        base.OnDragOver(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    private void OnControlDragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));

            if (GetChildAtPoint(p) == ddc)
                e.Effect = DragDropEffects.None;
            else
                e.Effect = DragDropEffects.Move;
        }
    }

Finally, call from the DragDrop method override and event the DropControl method and pass the DragEventArgs param.

    protected override void OnDragDrop(DragEventArgs e)
    {
        base.OnDragDrop(e);
        DropControl(e);
    }

    private void OnControlDragDrop(object sender, DragEventArgs e)
    {
        DropControl(e);
    }

The dropped control takes the index of the control under the mouse position if any otherwise it will be inserted at the end of the Controls collection.

    private void DropControl(DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));
            var child = GetChildAtPoint(p);
            var index = child == null
                ? Controls.Count
                : Controls.GetChildIndex(child);

            ddc.Parent = this;
            Controls.SetChildIndex(ddc, index);
        }
    }
}

Put it all together.

public class DragDropFlowLayoutPanel : FlowLayoutPanel
{
    public DragDropFlowLayoutPanel()
    {
        AllowDrop = true;            
    }

    [DefaultValue(true)]
    public override bool AllowDrop 
    {
        get =&gt; base.AllowDrop; 
        set =&gt; base.AllowDrop = value; 
    }

    protected override void OnControlAdded(ControlEventArgs e)
    {
        base.OnControlAdded(e);

        if (e.Control is DragDropControl)
        {
            e.Control.DragOver += OnControlDragOver;
            e.Control.DragDrop += OnControlDragDrop;
            e.Control.MouseDown += OnControlMouseDown;
        }
    }

    protected override void OnControlRemoved(ControlEventArgs e)
    {
        base.OnControlRemoved(e);

        e.Control.DragOver -= OnControlDragOver;
        e.Control.DragDrop -= OnControlDragDrop;
        e.Control.MouseDown -= OnControlMouseDown;
    }

    protected override void OnDragEnter(DragEventArgs e)
    {
        base.OnDragEnter(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        base.OnDragOver(e);

        if (e.Data.GetDataPresent(typeof(DragDropControl)))
            e.Effect = DragDropEffects.Move;
    }

    protected override void OnDragDrop(DragEventArgs e)
    {
        base.OnDragDrop(e);
        DropControl(e);
    }

    private void OnControlDragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));

            if (GetChildAtPoint(p) == ddc)
                e.Effect = DragDropEffects.None;
            else
                e.Effect = DragDropEffects.Move;
        }
    }

    private void OnControlDragDrop(object sender, DragEventArgs e)
    {
        DropControl(e);
    }

    private void OnControlMouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            var control = sender as DragDropControl;
            DoDragDrop(control, DragDropEffects.Move);
        }
    }

    private void DropControl(DragEventArgs e)
    {
        if (e.Data.GetData(typeof(DragDropControl)) is DragDropControl ddc)
        {
            var p = PointToClient(new Point(e.X, e.Y));
            var child = GetChildAtPoint(p);
            var index = child == null
                ? Controls.Count
                : Controls.GetChildIndex(child);

            ddc.Parent = this;
            Controls.SetChildIndex(ddc, index);
        }
    }
}

重新排列多个流式布局中的自定义控件

huangapple
  • 本文由 发表于 2023年2月16日 19:06:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75471363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定