拖动“删除”后,TabPage标题对齐错误

时间:2015-07-27 03:30:37

标签: c# winforms drag-and-drop tabcontrol tabpage

我有一个扩展System.Windows.Forms.TabControl的类,并为其TabPage实现了拖放机制,如下所示:

#region Overriden base methods

protected override void OnDragOver(DragEventArgs e)
{
    if (PointedTabPage == null) return;

    e.Effect = DragDropEffects.Move;

    var dragTab = e.Data.GetData(typeof (ManagedTabPage)) as ManagedTabPage;

    if (dragTab == null) return;

    int dropIndex = TabPages.IndexOf(PointedTabPage);
    int dragIndex = TabPages.IndexOf(dragTab);

    if (dragIndex == dropIndex) return;

    var modifiedTabPages = new List<ManagedTabPage>(from ManagedTabPage tabPage in TabPages
                                                    where TabPages.IndexOf(tabPage) != dragIndex
                                                    select TabPages[TabPages.IndexOf(tabPage)] as ManagedTabPage);

    modifiedTabPages.Insert(dropIndex, dragTab);

    for (byte i = 0; i < TabPages.Count; ++i)
    {
        var managedTabPage = TabPages[i] as ManagedTabPage;

        if (managedTabPage != null && managedTabPage.Uid == modifiedTabPages[i].Uid)
            continue;

        TabPages[i] = modifiedTabPages[i];
    }

    SelectedTab = dragTab;
}

protected override void OnMouseDown(MouseEventArgs e)
{
    try
    {
        switch (e.Button)
        {
            case MouseButtons.Left:
                DoDragDrop(PointedTabPage, DragDropEffects.Move);

                break;
            case MouseButtons.Middle:
                CloseTab(PointedTabPage);

                break;
        }
    }
    catch (InvalidOperationException)
    {
    }
    finally
    {
        TabPages.Insert(0, String.Empty);
        TabPages.RemoveAt(0);
    }
}

#endregion

Nota bene在finally方法的OnMouseDown条款中有2行用于解决问题 问题:出于某些原因,在拖动&#39; n'删除其标题的任何TabPage对齐后,没有这些行是错误的: draganddrop

如果没有这种有臭味的解决方法,我该怎么做才能纠正这种行为?也许发送一些Windows消息可以做到这一点? 非常感谢任何建议。

修改1。 ManagedTabPage代码100%不重要(它只是扩展TabPage并带有一些特定属性。)

PointedTabPage也不重要,但就是这样:

return (from ManagedTabPage tabPage in TabPages
        let tabPageIndex = TabPages.IndexOf(tabPage)
        where GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position))
        select TabPages[tabPageIndex]).Single() as ManagedTabPage;

我试图实现标签的完全居中对齐。你看,截图上的标签没有水平居中

2 个答案:

答案 0 :(得分:1)

我对发布的代码无能为力。让我们采用完全不同的方法,创建一个tabcontrol,支持使用鼠标拖动标签页。在项目中添加一个新类并粘贴此代码:

using System;
using System.Drawing;
using System.Windows.Forms;

class TabControlEx : TabControl {
    private Point downPos;
    private Form draggingHost;
    private Rectangle draggingBounds;
    private Point draggingPos;

    public TabControlEx() {
        this.SetStyle(ControlStyles.UserMouse, true);
    }
}

变量的使用后来变得清晰。我们需要的第一件事是从TabControl获取鼠标事件,以便我们可以看到用户试图拖动选项卡。这需要打开UserMouse控件样式,默认情况下它是内置Windows控件并使用自己的鼠标处理的控件(如TabControl)关闭。

使用Build&gt;构建新控件并将其从工具箱顶部拖到窗体上。所有内容仍然看起来像常规的TabControl,但请注意,单击选项卡不再更改活动选项卡。打开UserMouse样式的副作用。让我们先修复,粘贴:

protected override void OnMouseDown(MouseEventArgs e) {
    for (int ix = 0; ix < this.TabCount; ++ix) {
        if (this.GetTabRect(ix).Contains(e.Location)) {
            this.SelectedIndex = ix;
            break;
        }
    }
    downPos = e.Location;
    base.OnMouseDown(e);
}

我们正在存储点击位置,稍后需要检测用户拖动标签。这需要MouseMove事件,我们需要在用户将鼠标移动到远离原始点击位置的距离时开始拖动:

protected override void OnMouseMove(MouseEventArgs e) {
    if (e.Button == MouseButtons.Left && this.TabCount > 1) {
        var delta = SystemInformation.DoubleClickSize;
        if (Math.Abs(e.X - downPos.X) >= delta.Width ||
            Math.Abs(e.Y - downPos.Y) >= delta.Height) {
            startDragging();
        }
    }
    base.OnMouseMove(e);
}

startDragging方法需要创建一个可以用鼠标移动的顶层窗口,其中包含我们拖动的标签的传真。我们将它显示为一个拥有的窗口,因此它始终位于顶部,与选项卡控件的大小完全相同:

private void startDragging() {
    draggingBounds = this.RectangleToScreen(new Rectangle(Point.Empty, this.Size));
    draggingHost = createDraggingHost(draggingBounds);
    draggingPos = Cursor.Position;
    draggingHost.Show(this.FindForm());
}

createDraggingHost需要进行繁重的工作并创建一个看起来像标签的窗口。一个无边框的形式,我们将用鼠标移动。我们将使用TransparencyKey属性使其看起来类似于拖动的TabPage,顶部有一个标签。只需让它显示标签页的屏幕截图即可让它看起来一样:

private Form createDraggingHost(Rectangle bounds) {
    var host = new Form() { FormBorderStyle = FormBorderStyle.None, ControlBox = false, AutoScaleMode = AutoScaleMode.None, Bounds = this.draggingBounds, StartPosition = FormStartPosition.Manual };
    host.BackColor = host.TransparencyKey = Color.Fuchsia;
    var tabRect = this.GetTabRect(this.SelectedIndex);
    var tabImage = new Bitmap(bounds.Width, bounds.Height);
    using (var gr = Graphics.FromImage(tabImage)) {
        gr.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
        gr.FillRectangle(Brushes.Fuchsia, new Rectangle(0, 0, tabRect.Left, tabRect.Height));
        gr.FillRectangle(Brushes.Fuchsia, new Rectangle(tabRect.Right, 0, bounds.Width - tabRect.Right, tabRect.Height));
    }
    host.Capture = true;
    host.MouseCaptureChanged += host_MouseCaptureChanged;
    host.MouseUp += host_MouseCaptureChanged;
    host.MouseMove += host_MouseMove;
    host.Paint += (s, pe) => pe.Graphics.DrawImage(tabImage, 0, 0);
    host.Disposed += delegate { tabImage.Dispose(); };
    return host;
}

请注意Capture属性的使用,即我们检测用户释放鼠标按钮或通过任何其他方式中断操作的方式。我们将使用MouseMove事件来移动窗口:

private void host_MouseMove(object sender, MouseEventArgs e) {
    draggingHost.Location = new Point(draggingBounds.Left + Cursor.Position.X - draggingPos.X, 
                                      draggingBounds.Top + Cursor.Position.Y - draggingPos.Y);

}

最后我们需要处理拖拽的完成。我们将交换标签,将拖动的标签插入鼠标位置并销毁窗口:

private void host_MouseCaptureChanged(object sender, EventArgs e) {
    if (draggingHost.Capture) return;
    var pos = this.PointToClient(Cursor.Position);
    for (int ix = 0; ix < this.TabCount; ++ix) {
        if (this.GetTabRect(ix).Contains(pos)) {
            if (ix != this.SelectedIndex) {
                var page = this.SelectedTab;
                this.TabPages.RemoveAt(this.SelectedIndex);
                this.TabPages.Insert(ix, page);
                this.SelectedIndex = ix;
            }
            break;
        }
    }
    draggingHost.Dispose();
    draggingHost = null;
}

看起来很不错。

答案 1 :(得分:0)

由于您没有共享ManagedTabPage代码,我使用了默认TabPage控件

在方法OnDragOver

中进行了更改
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace Demo
{
    public class MyTabControl : TabControl
    {
        public MyTabControl()
        {
            SizeMode = TabSizeMode.Fixed; 
            ItemSize = new Size(224, 20);
        }
        #region Overriden base methods

        protected override void OnDragOver(DragEventArgs e)
        {
            if (DesignMode)
                return;
            if (PointedTabPage == null) return;

            e.Effect = DragDropEffects.Move;

            var dragTab = e.Data.GetData(typeof(TabPage)) as TabPage;

            if (dragTab == null) return;

            int dropIndex = TabPages.IndexOf(PointedTabPage);
            int dragIndex = TabPages.IndexOf(dragTab);

            if (dragIndex == dropIndex) return;

            // change position of tab
            TabPages.Remove(dragTab);
            TabPages.Insert(dropIndex, dragTab);

            SelectedTab = dragTab;

            base.OnDragOver(e);
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (DesignMode)
                return;

            switch (e.Button)
            {
                case MouseButtons.Left:
                    DoDragDrop(PointedTabPage, DragDropEffects.Move);                    

                    break;
                case MouseButtons.Middle:
                    TabPages.Remove(PointedTabPage);
                    break;
            }
        }

        #endregion
        TabPage PointedTabPage
        {
            get
            {
                return TabPages.OfType<TabPage>()
                    .Where((p, tabPageIndex) => GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position)))
                    .FirstOrDefault();
            }
        }
    }
}