C# 用tabcontrol实现窗体类似网页排版的显示

这里做的比较简陋,可以美化下

把form设置为非顶级控件,直接放在tabcontrol里边,然后实现tabcontrol的拖拽移除tabpage显示form以及添加tabpage

mousemove的触发时机需要优化一下

这里是比较简单的实现方式,也比较丑,可以实现像QQ那样的效果,可以重绘tabcontrol控件,然后以同样的方式实现拖拽移除显示和添加tabpage

也可以实现类似timQQ那样的形式,可以用自定义控件左边用listview右边放容器,重绘listview控件然后实现拖拽显示和添加,这样做复杂一点,但是会好看很多····

public Form1()
        {
            InitializeComponent();
        }
        private TabControl tabControl1 = new TabControl();
        private bool StartMove = false;
        private bool flag = true;
        private TabPage MovePage = null;
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            tabControl1.Dock = DockStyle.Fill;
            
            this.Controls.Add(tabControl1);
            this.Controls.SetChildIndex(tabControl1, 0);
            frm1ToolStripMenuItem.Click += delegate {
                var frm1 = new Form();
                frm1.Text = "frm1";
                var btn=new Button();
                btn.Text = "btn";
                btn.Click += delegate { MessageBox.Show("this is frm1"); btn.Text = "frm1"; };
                frm1.Controls.Add(btn);
                addfrm(frm1);
            };
            frm2ToolStripMenuItem.Click += delegate
            {
                var frm1 = new Form();
                frm1.Text = "frm2";
                var btn = new Button();
                btn.Text = "btn";
                btn.Click += delegate { MessageBox.Show("this is frm2"); };
                frm1.Controls.Add(btn);
                addfrm(frm1);
            };
            frm3ToolStripMenuItem.Click += delegate
            {
                var frm1 = new Form();
                frm1.Text = "frm3";
                var btn = new Button();
                btn.Text = "btn";
                btn.Click += delegate { MessageBox.Show("this is frm3"); };
                frm1.Controls.Add(btn);
                addfrm(frm1);
            };
            tabControl1.AllowDrop = true;
            Func<Point, TabPage> GetTabPageByTab = (point) =>
            {
                for (int i = 0; i < this.tabControl1.TabPages.Count; i++)
                {
                    if (tabControl1.GetTabRect(i).Contains(point))
                    {
                        return this.tabControl1.TabPages[i];
                    }
                }
                return null;
            };
            tabControl1.MouseDown += (o, eg) => {
                if (flag)
                {
                    StartMove = true;
                    MovePage=GetTabPageByTab(new Point(eg.X, eg.Y));
                }
                flag = true;
            };
            tabControl1.MouseUp += (o, eg) => {
                StartMove = false;
            };
            tabControl1.SelectedIndexChanged += (o, eg) => {
                flag = false;//切换的时候因为失去焦点的原因,会触发down事件不触发up事件这里做屏蔽
            };
            tabControl1.MouseMove += (o, eg) => {
                if (StartMove && flag)
                {
                    if (MovePage != null)
                    {
                        this.DoDragDrop(MovePage, DragDropEffects.None);
                    }
                }
            };
            tabControl1.ControlAdded += delegate {
                StartMove = false;
                flag = false;
            };
            tabControl1.DragOver += (o, eg) => {
                eg.Effect = DragDropEffects.None;
                if (tabControl1.TabPages.Count < 2) return;
                TabPage page = (TabPage)eg.Data.GetData(typeof(TabPage));
                var frm1 = page.Controls[0] as Form;
                frm1.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
                frm1.Parent = null;
                frm1.TopLevel = true;
                frm1.Owner = this;
                frm1.Location = new Point(eg.X, eg.Y);
                frm1.Show();
                tabControl1.TabPages.Remove(page);
                MovePage = null;
                frm1.Move += (oo, ee) =>
                {
                    for (int i = 0; i < tabControl1.TabPages.Count; i++)
                    {
                        if (tabControl1.GetTabRect(i).Contains(this.PointToClient(frm1.Location)))
                        {
                            addfrm(frm1);
                        }
                    }
                };

            };
           
        }
        private void addfrm(Form frm)
        {
            if (tabControl1.Visible == false) tabControl1.Visible = true;
            bool flag = true;
            frm.TopLevel = false;
            frm.Dock = DockStyle.Fill;
            frm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            frm.Owner = null;
            foreach (TabPage page in tabControl1.TabPages)
            {
                if (page.Controls.Count < 1)
                {
                    page.Controls.Add(frm);
                    page.Text=frm.Text;
                    flag = false;
                    frm.Show();
                    return;
                }
            }
            if (flag)
            {
                var page=new TabPage(frm.Text);
                page.Controls.Add(frm);
                tabControl1.TabPages.Add(page);
                frm.Show();
            }
          
        }

之前闲的没事写的tabcontrol加载窗体,刚好最近要用到这个· 这里简单重绘一个·因为要该背景色 去边框之类的,网上查了很多·不太好使· 这里用简单粗暴的方式直接吧背景色刷成想要的 然后在填充其他要改变背景色的地方

using System.Windows.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExerciseUIPrj.controls
{
    public partial class XTabControl : TabControl
    {
        Size defaultSize = new Size();
        const int dheight = 10;
        public XTabControl()
        {
            InitializeComponent();
            base.SetStyle(
                     ControlStyles.UserPaint |                      // 控件将自行绘制,而不是通过操作系统来绘制  
                     ControlStyles.OptimizedDoubleBuffer |          // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁  
                     ControlStyles.AllPaintingInWmPaint |           // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁  
                     ControlStyles.ResizeRedraw |                   // 在调整控件大小时重绘控件  
                     ControlStyles.SupportsTransparentBackColor,    // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明  
                     true);                                         // 设置以上值为 true  
           base.UpdateStyles();
            this.SizeMode = TabSizeMode.Fixed;  // 大小模式为固定  
            FontChanged += XTabControl_FontChanged;
            defaultSize = TextRenderer.MeasureText("tabpage1", Font);
            Margin = new System.Windows.Forms.Padding(0);
            Padding = new Point(0, 0);
            Appearance = TabAppearance.Normal;
            SetItemSize();
        }
        //public override Rectangle DisplayRectangle
        //{
        //    get
        //    {
        //        Rectangle rect = base.DisplayRectangle;
        //        return new Rectangle(rect.Left - 2, rect.Top-2, rect.Width + 4, rect.Height + 4);
        //    }
        //}
        public void SetItemSize()
        {
            if (Width >= 0 && TabCount > 0)
            {
                if (Alignment ==TabAlignment.Top || Alignment == TabAlignment.Bottom)
                {
                    int width = (int)((Size.Width - 5) / (double)this.TabCount);
                    this.ItemSize = new Size(width, defaultSize.Height + dheight);   // 设定每个标签的尺寸 
                }
                else
                {
                    int width = defaultSize.Width;
                    foreach (TabPage t in TabPages)
                    {
                        var w = TextRenderer.MeasureText(t.Name, Font).Width;
                        width = w > width ? w : width;
                    }
                    width = width + dheight;
                    this.ItemSize = new Size(defaultSize.Height + dheight, width);   // 设定每个标签的尺寸 //这里w和h是反的
                }

            }
        }

        private void XTabControl_FontChanged(object sender, EventArgs e)
        {
            defaultSize = TextRenderer.MeasureText("tabpage1", Font);
            SetItemSize();
        }
        Color dbackColor = Color.FromArgb(220, 220, 220);
        protected override void OnPaint(PaintEventArgs pe)
        {
            var g = pe.Graphics;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            g.FillRectangle(new SolidBrush(Color.White), pe.ClipRectangle);//所有背景全刷,直接刷成白色······
            Rectangle rect = new Rectangle();
            
            if (Alignment == TabAlignment.Left || Alignment == TabAlignment.Right)
            {
                rect = new Rectangle(0,0, ItemSize.Height+2, Height);
                g.FillRectangle(new SolidBrush(dbackColor), rect);//吧标签背景色统一填充了
            }
            else
            {
                 rect = new Rectangle(0, 0, Width, ItemSize.Height+2);
               // g.FillRectangle(new SolidBrush(dbackColor), rect);
            }

            for (int i = 0; i < this.TabCount; i++)//这里画标签,可以在里边画图片···这里只画文字···
            {
                Rectangle bounds = this.GetTabRect(i);
                if (SelectedIndex == i)
                {
                    g.FillRectangle(new SolidBrush(Color.White), bounds);
                }
                else
                {
                    g.FillRectangle(new SolidBrush(dbackColor), bounds);
                }
                PointF textPoint = new PointF();
                SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font);
                textPoint.X= bounds.X + (bounds.Width - textSize.Width) / 2;
                textPoint.Y = bounds.Y + (bounds.Height - textSize.Height) / 2;
                g.DrawString( this.TabPages[i].Text,   this.Font, SystemBrushes.ControlText, textPoint.X,  textPoint.Y);
            }
        }
    }
}