Можно ли сделать так, чтобы элемент управления вкладками WinForms мог переупорядочивать вкладки, как IE или Firefox? - PullRequest
9 голосов
/ 04 декабря 2010

Можно ли изменить порядок вкладок в WinForms TabControl во время выполнения, например, IE или Firefox?

Такие ссылки не дают мне особой надежды.

Ответы [ 4 ]

9 голосов
/ 04 декабря 2010

Конечно, это возможно! Скорее всего, вы пытаетесь усложнить решение. По сути, все, что вам нужно сделать, это создать подкласс стандарта TabControl и добавить немного логики в обработчики событий мыши. Вам просто нужно проверить, какую форму пользователь в данный момент перетаскивает, и изменить ее порядок в коллекции TabPages.

В сети доступно несколько полных решений:

6 голосов
/ 06 июля 2012

Я нашел решение , первоначально опубликованное @Cody Grey, главным образом тем, что я хотел, но я не видел, чтобы оно было настолько сложным.

Это моё упрощение, реализованное производным от TabControl:

public class DraggableTabControl : TabControl
{
    private TabPage m_DraggedTab;

    public DraggableTabControl()
    {
        MouseDown += OnMouseDown;
        MouseMove += OnMouseMove;
    }

    private void OnMouseDown(object sender, MouseEventArgs e)
    {
        m_DraggedTab = TabAt(e.Location);
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left || m_DraggedTab == null)
        {
            return;
        }

        TabPage tab = TabAt(e.Location);

        if (tab == null || tab == m_DraggedTab)
        {
            return;
        }

        Swap(m_DraggedTab, tab);
        SelectedTab = m_DraggedTab;
    }

    private TabPage TabAt(Point position)
    {
        int count = TabCount;

        for (int i = 0; i < count; i++)
        {
            if (GetTabRect(i).Contains(position))
            {
                return TabPages[i];
            }
        }

        return null;
    }

    private void Swap(TabPage a, TabPage b)
    {
        int i = TabPages.IndexOf(a);
        int j = TabPages.IndexOf(b);
        TabPages[i] = b;
        TabPages[j] = a;
    }
}

API перетаскивания действительно предназначены для перетаскивания содержимого между отдельными приложениями или, по крайней мере, отдельными элементами управления. Использование их в этом случае является излишним.

Убедитесь, что вы также проголосуете за ответ Коди, если вы проголосуете за мой, так как он основан на его.

1 голос
/ 01 июля 2016

Я немного расширил ответ Джейкоба Стэнли. Таким образом, обмен не будет происходить слишком часто. Это особенно полезно для вкладок разных размеров, и в этом случае предыдущее решение очень часто менялось при перетаскивании.

Разница в восприятии пользователя заключается в том, что вам нужно немного перетащить, чтобы фактически переместить вкладку. Но это похоже на переупорядочивание вкладок в браузерах.

Также я добавил курсор при перетаскивании и включил двойную буферизацию.

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

namespace Controls
{
    public class DraggableTabControl : TabControl
    {
        private TabPage draggedTab;

        public DraggableTabControl()
        {
            this.MouseDown += OnMouseDown;
            this.MouseMove += OnMouseMove;
            this.Leave += new System.EventHandler(this.DraggableTabControl_Leave);

            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
        }

        private void OnMouseDown(object sender, MouseEventArgs e)
        {
            draggedTab = TabAt(e.Location);
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left || draggedTab == null)
            {
                this.Cursor = this.DefaultCursor;
                draggedTab = null;
                return;
            }

            int index = TabPages.IndexOf(draggedTab);          
            int nextIndex = index + 1;
            int prevIndex = index - 1;

            int minXForNext = int.MaxValue;
            int maxXForPrev = int.MinValue;

            var tabRect = GetTabRect(index);

            if (nextIndex < TabPages.Count)
            {
                var nextTabRect = GetTabRect(nextIndex);
                minXForNext = tabRect.Left + nextTabRect.Width;
            }

            if (prevIndex >= 0)
            {
                var prevTabRect = GetTabRect(prevIndex);
                maxXForPrev = prevTabRect.Left + tabRect.Width;
            }

            this.Cursor = Cursors.Hand;

            if (e.Location.X > maxXForPrev && e.Location.X < minXForNext)
            {
                return;
            }

            TabPage tab = TabAt(e.Location);

            if (tab == null || tab == draggedTab)
            {
                return;
            }

            Swap(draggedTab, tab);
            SelectedTab = draggedTab;
        }

        private TabPage TabAt(Point position)
        {
            int count = TabCount;

            for (int i = 0; i < count; i++)
            {
                if (GetTabRect(i).Contains(position))
                {
                    return TabPages[i];
                }
            }

            return null;
        }

        private void Swap(TabPage a, TabPage b)
        {
            int i = TabPages.IndexOf(a);
            int j = TabPages.IndexOf(b);

            TabPages[i] = b;
            TabPages[j] = a;
        }

        private void DraggableTabControl_Leave(object sender, EventArgs e)
        {
            this.Cursor = this.DefaultCursor;
            draggedTab = null;
        }
    }
}
1 голос
/ 20 мая 2011

переупорядочение вкладок с помощью перетаскивания - от Людвига Б.
вдохновленный http://dotnetrix.co.uk/tabcontrol.htm#tip7

        private void tc_MouseDown(object sender, MouseEventArgs e)
        {
            // store clicked tab
            TabControl tc = (TabControl)sender;
            int hover_index = this.getHoverTabIndex(tc);
            if (hover_index >= 0) { tc.Tag = tc.TabPages[hover_index]; }
        }
        private void tc_MouseUp(object sender, MouseEventArgs e)
        {
            // clear stored tab
            TabControl tc = (TabControl)sender;
            tc.Tag = null;
        }
        private void tc_MouseMove(object sender, MouseEventArgs e)
        {           
            // mouse button down? tab was clicked?
            TabControl tc = (TabControl)sender;
            if ((e.Button != MouseButtons.Left) || (tc.Tag == null)) return;
            TabPage clickedTab = (TabPage)tc.Tag;
            int clicked_index = tc.TabPages.IndexOf(clickedTab);

            // start drag n drop
            tc.DoDragDrop(clickedTab, DragDropEffects.All);
        }
        private void tc_DragOver(object sender, DragEventArgs e)
        {
            TabControl tc = (TabControl)sender;

            // a tab is draged?
            if (e.Data.GetData(typeof(TabPage)) == null) return;
            TabPage dragTab = (TabPage)e.Data.GetData(typeof(TabPage));
            int dragTab_index = tc.TabPages.IndexOf(dragTab);

            // hover over a tab?
            int hoverTab_index = this.getHoverTabIndex(tc);
            if (hoverTab_index < 0) { e.Effect = DragDropEffects.None; return; }
            TabPage hoverTab = tc.TabPages[hoverTab_index];
            e.Effect = DragDropEffects.Move;

            // start of drag?
            if (dragTab == hoverTab) return;

            // swap dragTab & hoverTab - avoids toggeling
            Rectangle dragTabRect = tc.GetTabRect(dragTab_index);
            Rectangle hoverTabRect = tc.GetTabRect(hoverTab_index);

            if (dragTabRect.Width < hoverTabRect.Width)
            {
                Point tcLocation = tc.PointToScreen(tc.Location);

                if (dragTab_index < hoverTab_index)
                {
                    if ((e.X - tcLocation.X) > ((hoverTabRect.X + hoverTabRect.Width) - dragTabRect.Width))
                        this.swapTabPages(tc, dragTab, hoverTab);
                }
                else if (dragTab_index > hoverTab_index)
                {
                    if ((e.X - tcLocation.X) < (hoverTabRect.X + dragTabRect.Width))
                        this.swapTabPages(tc, dragTab, hoverTab);
                }
            }
            else this.swapTabPages(tc, dragTab, hoverTab);

            // select new pos of dragTab
            tc.SelectedIndex = tc.TabPages.IndexOf(dragTab);
        }

        private int getHoverTabIndex(TabControl tc)
        {
            for (int i = 0; i < tc.TabPages.Count; i++)
            {
                if (tc.GetTabRect(i).Contains(tc.PointToClient(Cursor.Position)))
                    return i;
            }

            return -1;
        }

        private void swapTabPages(TabControl tc, TabPage src, TabPage dst)
        {
            int index_src = tc.TabPages.IndexOf(src);
            int index_dst = tc.TabPages.IndexOf(dst);
            tc.TabPages[index_dst] = src;
            tc.TabPages[index_src] = dst;
            tc.Refresh();
        }
...