WPF 简易可拖动的TabControl

WPF 简易可拖动的TabControl
代码也不长,就400来行,可添加、删除、拖动Item项,仿照vs2019的样子,
字体、大小、颜色、删除询问、右键询问等均可自定义。
不写xaml,全部后台代码。根据需要自行扩展,如有错误自行修正。

代码

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public class TabControl : UserControl
{
    public class Item
    {
        public int id;
        public Canvas Title_Panel;
        public TextBlock title_box;
        public Canvas close_box;
        public int title_width;
        public string title_text;
        public Canvas content;
        public Item(int i, string t) { id = i; title_text = t; }
    }
    public static List<Item> items = new List<Item>();

    public string Item_Font = "微软雅黑";
    public double Item_Size = 12.0;
    public double Item_Height = 23.0;
    public double Move_Opacity = 0.7;
    public Color BackColor = Color.FromArgb(0xff, 0x29, 0x39, 0x56);
    public Color Select_PressColor = Color.FromArgb(0xff, 0xf1, 0xc9, 0x83);
    public Color Select_PressTextColor = Colors.Black;
    public Color CloseButton_PressColor = Color.FromArgb(0xff, 0x26, 0x26, 0x26);
    public Color Select_LeaveColor = Color.FromArgb(0xff, 0x3c, 0x53, 0x73);
    public Color Select_LeaveTextColor = Colors.White;
    public Color Move_Color = Color.FromArgb(0xff, 0x5b, 0x71, 0x99);
    public Color CloseButton_LeaveColor = Colors.Gray;
    public event EventHandler CloseMouseDown;
    public event MouseButtonEventHandler TableMouseRightDown;
    public bool Remove_Flag;
    public int SelectIndex = 0;
    public int MoveIndex = 0;
    public bool Visable_Close = true;
    public bool Visable_Menu = true;

    private Canvas tab_control, Separator_Line;
    private bool isDragging = false;
    private int Content_Visable = 0;
    private ContextMenu ItemMenu = new ContextMenu();

    public void Initialize_Control(Canvas obj, double width, double height, double left, double top)
    {
        tab_control = new Canvas()
        {
            Width = width,
            Height = height,
            Clip = new RectangleGeometry(new Rect(0, 0, width, height)),
            Background = new SolidColorBrush(BackColor)
        };
        tab_control.SetValue(Canvas.LeftProperty, left);
        tab_control.SetValue(Canvas.TopProperty, top);

        Separator_Line = new Canvas()
        {
            Width = width,
            Height = 2,
            Background = new SolidColorBrush(Select_PressColor)
        };
        Separator_Line.SetValue(Canvas.LeftProperty, 0.0);
        Separator_Line.SetValue(Canvas.TopProperty, Item_Height);
        tab_control.Children.Add(Separator_Line);

        if (Visable_Menu)
        {
            double[] size = ObtainCharSize("◣", Item_Font, Item_Size);
            Canvas Menu_Box = new Canvas()
            {
                Width = size[0] + 10,
                Height = Item_Height,
                Opacity = 0.8,
                Background = new SolidColorBrush(Move_Color),
            };
            Menu_Box.MouseLeftButtonUp += ItemMenu_MouseLeftButtonUp;
            Menu_Box.SetValue(Canvas.LeftProperty, width - Menu_Box.Width);
            Menu_Box.SetValue(Canvas.TopProperty, 0.0);
            Panel.SetZIndex(Menu_Box, 1000);
            tab_control.Children.Add(Menu_Box);

            TextBlock Menu_Button = new TextBlock()
            {
                Width = size[0],
                Height = size[1],
                Text = "◣",
                FontSize = Item_Size,
                Foreground = new SolidColorBrush(Select_LeaveTextColor),
                Background = Brushes.Transparent,
            };
            Menu_Button.SetValue(Canvas.LeftProperty, (Menu_Box.Width - size[0]) / 2);
            Menu_Button.SetValue(Canvas.TopProperty, (Item_Height - size[1]) / 2);
            Menu_Box.Children.Add(Menu_Button);
        }

        ItemMenu.Opacity = 0.91;

        obj.Children.Add(tab_control);
    }

    private void ItemMenu_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        ItemMenu.Items.Clear();
        for (int i = 0; i < items.Count; i++)
        {
            MenuItem menuItem = new MenuItem()
            {
                Header = items[i].title_text,
                Icon = new Image { Source = new BitmapImage(new Uri("pack://application:,,,/images/apply.png")) },
                Tag = i
            };
            menuItem.Click += MenuItem_Click;
            ItemMenu.Items.Add(menuItem);
        }
        ItemMenu.IsOpen = true;
    }

    private void MenuItem_Click(object sender, RoutedEventArgs e)
    {
        MenuItem pL = (MenuItem)sender;

        if ((int)pL.Tag != SelectIndex) Set_Index((int)pL.Tag);
    }

    public void Add_Item(string item_text, FrameworkElement userControl)
    {
        items.Add(new Item(items.Count, item_text));
        int fwidth = 0;
        double fheight = 0;
        double[] size = new double[2];
        for (int i = 0; i < item_text.Length; i++)
        {
            size = ObtainCharSize(item_text.Substring(i, 1), Item_Font, Item_Size);
            fwidth += (int)size[0];
            fheight = fheight < size[1] ? size[1] : fheight;
        }

        double left = 0;
        for (int i = 0; i < items.Count; i++) left += items[i].title_width;
        items[items.Count - 1].Title_Panel = new Canvas()
        {
            Width = fwidth + 20,
            Height = Item_Height,
            Background = Brushes.Transparent,
            Tag = items.Count - 1
        };
        items[items.Count - 1].Title_Panel.SetValue(Canvas.LeftProperty, left);
        items[items.Count - 1].Title_Panel.SetValue(Canvas.TopProperty, 0.0);

        items[items.Count - 1].title_box = new TextBlock()
        {
            Text = item_text,
            Width = fwidth + 20,
            Height = Item_Height,
            Padding = new Thickness(10, (Item_Height - fheight) / 2, 0, 0),
            Background = new SolidColorBrush(Select_LeaveColor),
            FontFamily = new FontFamily(Item_Font),
            FontSize = Item_Size,
            Foreground = new SolidColorBrush(Select_LeaveTextColor),
        };
        items[items.Count - 1].Title_Panel.Children.Add(items[items.Count - 1].title_box);
        tab_control.Children.Add(items[items.Count - 1].Title_Panel);

        Thumb thumb_title = new Thumb()
        {
            Width = fwidth + 20,
            Height = Item_Height,
            Opacity = 0,
            Tag = items.Count - 1
        };
        Panel.SetZIndex(thumb_title, 100);
        items[items.Count - 1].Title_Panel.Children.Add(thumb_title);
        thumb_title.PreviewMouseLeftButtonDown += Title_PreviewMouseLeftButtonDown;
        thumb_title.PreviewMouseLeftButtonUp += Title_PreviewMouseLeftButtonUp;
        thumb_title.PreviewMouseMove += Thumb_PreviewMouseMove;
        thumb_title.MouseLeave += Thumb_title_MouseLeave;

        if (TableMouseRightDown != null) thumb_title.AddHandler(PreviewMouseRightButtonDownEvent, new MouseButtonEventHandler(TableMouseRightDown));
        thumb_title.DragDelta += Title_DragDelta;

        double[] fs = ObtainCharSize("×", Item_Font, Item_Size);
        items[items.Count - 1].close_box = new Canvas()
        {
            Height = Item_Height,
            Width = (int)fs[0] + 8,
            Background = new SolidColorBrush(Select_LeaveColor),
            Tag = items.Count - 1
        };
        items[items.Count - 1].close_box.PreviewMouseMove += Thumb_PreviewMouseMove;
        items[items.Count - 1].close_box.MouseLeave += Thumb_title_MouseLeave;

        TextBlock close_flag = new TextBlock()
        {
            Text = "×",
            Height = Item_Height,
            Width = (int)fs[0] + 5,
            Padding = new Thickness(2, (Item_Height - fheight) / 2, 0, 0),
            FontFamily = new FontFamily(Item_Font),
            FontSize = Item_Size,
            Foreground = new SolidColorBrush(CloseButton_LeaveColor),
            Background = Brushes.Transparent,
            Cursor = Cursors.Hand,
            Tag = items.Count - 1,
        };
        items[items.Count - 1].close_box.Children.Add(close_flag);
        close_flag.PreviewMouseLeftButtonUp += Close_MouseLeftButtonUp;
        if (Visable_Close)
        {
            items[items.Count - 1].close_box.SetValue(Canvas.LeftProperty, left + items[items.Count - 1].title_box.Width);
            items[items.Count - 1].close_box.SetValue(Canvas.TopProperty, 0.0);
            tab_control.Children.Add(items[items.Count - 1].close_box);
            fwidth += (int)fs[0];
            items[items.Count - 1].title_width = (int)(items[items.Count - 1].title_box.Width + items[items.Count - 1].close_box.Width + 1);
        }
        else items[items.Count - 1].title_width = (int)(items[items.Count - 1].title_box.Width + 1);

        items[items.Count - 1].content = new Canvas()
        {
            Width = tab_control.Width,
            Height = tab_control.Height - Item_Height - 2,
            Clip = new RectangleGeometry(new Rect(0, 0, tab_control.Width, tab_control.Height - Item_Height - 2)),
            Background = new SolidColorBrush(Colors.Transparent),
            Tag = items.Count - 1,
        };
        items[items.Count - 1].content.Children.Add(userControl);
        items[items.Count - 1].content.SetValue(Canvas.LeftProperty, 0.0);
        items[items.Count - 1].content.SetValue(Canvas.TopProperty, Item_Height + 2);
        items[items.Count - 1].content.Visibility = SelectIndex == items.Count - 1 ? Visibility.Visible : Visibility.Collapsed;
        tab_control.Children.Add(items[items.Count - 1].content);
        Set_Select(SelectIndex, true);
    }

    private void Thumb_title_MouseLeave(object sender, MouseEventArgs e)
    {
        FrameworkElement pL = (FrameworkElement)sender;

        if (!isDragging)
        {
            if (SelectIndex != (int)pL.Tag)
            {
                if ((int)pL.Tag >= 0 && (int)pL.Tag < items.Count)
                {
                    items[(int)pL.Tag].title_box.Background = new SolidColorBrush(Select_LeaveColor);
                    if (Visable_Close) items[(int)pL.Tag].close_box.Background = new SolidColorBrush(Select_LeaveColor);
                }
            }
        }
    }

    private void Thumb_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        FrameworkElement pL = (FrameworkElement)sender;
        MoveIndex = (int)pL.Tag;
        if (!isDragging)
        {
            if (SelectIndex != (int)pL.Tag)
            {
                items[(int)pL.Tag].title_box.Background = new SolidColorBrush(Move_Color);
                if (Visable_Close) items[(int)pL.Tag].close_box.Background = new SolidColorBrush(Move_Color);
            }
        }
    }

    private void Title_DragDelta(object sender, DragDeltaEventArgs e)
    {
        Thumb pL = (Thumb)sender;
        if (items.Count > 1)
        {
            Content_Visable = SelectIndex + 1 > items.Count - 1 ? SelectIndex - 1 : SelectIndex + 1;
            items[Content_Visable].content.Visibility = Visibility.Visible;
            Panel.SetZIndex(items[Content_Visable].content, 0);
        }
        Panel.SetZIndex(pL.Parent as Canvas, 99);
        if (Visable_Close) Panel.SetZIndex(items[(int)pL.Tag].close_box, 99);
        Panel.SetZIndex(items[(int)pL.Tag].content, 99);
        double left = 0;
        for (int i = 0; i < items.Count; i++)
        {
            if (i != SelectIndex)
            {
                Canvas.SetLeft(items[i].Title_Panel, left);
                if (Visable_Close) items[i].close_box.SetValue(Canvas.LeftProperty, left + items[i].title_box.Width);
                left += items[i].title_width;
            }
        }
        items[(int)pL.Tag].title_box.Opacity = Move_Opacity;
        if (Visable_Close) items[(int)pL.Tag].close_box.Opacity = Move_Opacity;
        items[(int)pL.Tag].content.Opacity = Move_Opacity;
        isDragging = true;
        Canvas.SetLeft(pL.Parent as Canvas, Canvas.GetLeft(pL.Parent as Canvas) + e.HorizontalChange);
        if (Visable_Close) Canvas.SetLeft(items[(int)pL.Tag].close_box, Canvas.GetLeft(items[(int)pL.Tag].close_box) + e.HorizontalChange);
        Canvas.SetLeft(items[(int)pL.Tag].content, Canvas.GetLeft(pL.Parent as Canvas) + e.HorizontalChange);
    }

    private void Title_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        Thumb pL = (Thumb)sender;
        Panel.SetZIndex(pL.Parent as Canvas, 0);
        if (Visable_Close) Panel.SetZIndex(items[(int)pL.Tag].close_box, 0);
        Panel.SetZIndex(items[(int)pL.Tag].content, 0);

        if (isDragging) // move
        {
            if (items.Count > 1)
            {
                items[Content_Visable].content.Visibility = Visibility.Collapsed;
            }
            items[(int)pL.Tag].title_box.Opacity = 1;
            if (Visable_Close) items[(int)pL.Tag].close_box.Opacity = 1;
            items[(int)pL.Tag].content.Opacity = 1;
            Swap_Item();
        }
    }

    private void Title_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Thumb pL = (Thumb)sender;
        isDragging = false;
        Set_Index((int)pL.Tag);
    }

    private void Close_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        TextBlock pL = (TextBlock)sender;
        Remove_Flag = true;
        CloseMouseDown?.Invoke(sender, e);
        if (Remove_Flag) Delete_Index((int)pL.Tag);
    }

    public void Delete_Index(int num)
    {
        if (items.Count == 1) return;
        Set_Select(SelectIndex, false);
        int delete = SelectIndex; ;
        if (num < SelectIndex) delete = SelectIndex - 1;
        else
        if (num == SelectIndex)
        {
            if (num == 0) delete = 0;
            else if (num == items.Count - 1)
            {
                delete = items.Count - 2;
            }
        }
        num = num > items.Count - 1 ? items.Count - 1 : num;
        tab_control.Children.Remove(items[num].Title_Panel);
        if (Visable_Close) tab_control.Children.Remove(items[num].close_box);
        tab_control.Children.Remove(items[num].content);
        items.RemoveAt(num);
        ArrangeMent();
        delete = delete > items.Count - 1 ? items.Count - 1 : delete;
        Set_Select(delete, true);
        SelectIndex = delete;
    }

    private void Swap_Item()
    {
        double swap_point = Canvas.GetLeft(items[SelectIndex].Title_Panel);
        double sum_width = items.Sum(w => w.title_width) - items[SelectIndex].title_width;
        int place = 0;
        if (swap_point < 0) place = 0;
        else
        if (swap_point > sum_width) place = items.Count - 1;
        else
        {
            for (int i = 0; i < items.Count; i++) //must no Linq
            {
                if ((swap_point > Canvas.GetLeft(items[i].Title_Panel)) &&
                    (swap_point < Canvas.GetLeft(items[i].Title_Panel) + items[i].title_width))
                {
                    place = i;
                    break;
                }
            }
        }
        var swap_item = items[SelectIndex];
        items.RemoveAt(SelectIndex);
        items.Insert(place, swap_item);
        ArrangeMent();
        Set_Index(place);
    }

    public void Set_Index(int num)
    {
        Set_Select(SelectIndex, false);
        Set_Select(num, true);
        SelectIndex = num;
    }

    private void Set_Select(int num, bool select)
    {
        if (select)
        {
            items[num].title_box.Background = new SolidColorBrush(Select_PressColor);
            items[num].title_box.Foreground = new SolidColorBrush(Select_PressTextColor);
            if (Visable_Close)
            {
                items[num].close_box.Background = new SolidColorBrush(Select_PressColor);
                (items[num].close_box.Children[0] as TextBlock).Foreground = new SolidColorBrush(CloseButton_PressColor);
            }
            items[num].content.Visibility = Visibility.Visible;
        }
        else
        {
            items[num].title_box.Background = new SolidColorBrush(Select_LeaveColor);
            items[num].title_box.Foreground = new SolidColorBrush(Select_LeaveTextColor);
            if (Visable_Close)
            {
                items[num].close_box.Background = new SolidColorBrush(Select_LeaveColor);
                (items[num].close_box.Children[0] as TextBlock).Foreground = new SolidColorBrush(CloseButton_LeaveColor);
            }
            items[num].content.Visibility = Visibility.Collapsed;
        }
    }

    private void ArrangeMent()
    {
        double left = 0;
        for (int i = 0; i < items.Count; i++)
        {
            items[i].Title_Panel.SetValue(Canvas.LeftProperty, left);
            if (Visable_Close)
            {
                items[i].close_box.SetValue(Canvas.LeftProperty, left + items[i].title_box.Width);
                items[i].close_box.Tag = i;
            }
            items[i].Title_Panel.Tag = i;
            (items[i].Title_Panel.Children[1] as Thumb).Tag = i;
            items[i].content.SetValue(Canvas.LeftProperty, 0.0);
            left += items[i].title_width;
        }
    }

    private double[] ObtainCharSize(string content, string ftype, double fsize)
    {
        double[] size = new double[2];
        content = content == " " ? "`" : content;
        FormattedText formattext = new FormattedText(content, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(ftype), fsize, Brushes.Black);
        size[0] = formattext.Width;
        size[1] = formattext.Height;
        return size;
    }
}

使用
随意定义一些UserControl,这里举例定义设置不同颜色文字的UserControl

public partial class MainWindow : Window
    {
        TabControl tabControl;
        U1 uc = new U1("000",Colors.OliveDrab);
        U1 uc1 = new U1("111", Colors.DimGray);
        U1 uc2 = new U1("222", Colors.Salmon);
        U1 uc3 = new U1("333", Colors.SteelBlue);
        U1 uc4 = new U1("444", Colors.AliceBlue);
        U1 uc5 = new U1("555", Colors.Green);
        U1 uc6 = new U1("666", Colors.LightPink);

        public MainWindow()
        {
            InitializeComponent();

            tabControl = new TabControl();
            //tabControl.BackColor = new SolidColorBrush(Colors.SteelBlue); // 背景色
            //tabControl.TableMouseRightDown += TabControl_TableMouseRightDown; // item上点击右键事件
            //tabControl.Visable_Menu = false; // 显示右侧项目菜单

            tabControl.Initialize_Control(MainCanvan, 600.0, 400.0,10,10); // grid里面的Canvas为容器
            tabControl.Add_Item("0编辑 Alt-E   ", uc);
            tabControl.Add_Item("1456编辑 Alt-E   ", uc1);
            tabControl.Add_Item("2EABC   ", uc2);
            tabControl.Add_Item("3EABC   ", uc3);
            tabControl.Add_Item("4ABC   ", uc4);
        }
    }
WPF是一种强大的UI开发框架,支持可拖动和改变大小的布局。在WPF中,我们可以通过使用特定的容器和控件来实现这些功能。 首先,WPF提供了一种名为Grid的容器,它可以帮助我们实现多行多列的布局。通过将控件放置在不同的行和列中,我们可以创建一个灵活的布局。该容器还支持通过鼠标拖动来改变控件的大小。我们可以在Grid的子元素上设置通行(RowSpan)和跨列(ColumnSpan)属性,以控制控件在布局中的位置和大小。 此外,WPF还提供了DockPanel和StackPanel等布局控件,它们可以帮助我们实现可拖动和改变大小的布局。DockPanel可以将控件停靠在容器的不同边缘,而StackPanel则可以在水平或垂直方向上堆叠控件。我们可以通过设置控件在布局中的停靠方式和堆叠方向,以及设置控件的可变大小属性,从而实现拖动和改变大小的效果。 除了容器控件,WPF还提供了一些特定的可拖动和改变大小的控件,如Thumb和ResizeGrip。Thumb是一种可拖动的小部件,我们可以将其放置在需要进行拖动的控件的边缘或角落。ResizeGrip是一种用于改变控件大小的小部件,我们可以将其放置在需要进行大小调整的控件的边缘。 总之,WPF提供了多种方法来实现可拖动和改变大小的布局。我们可以通过使用Grid、DockPanel、StackPanel等容器控件,以及Thumb和ResizeGrip等特定控件,灵活地创建出满足需求的布局效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值