1.c#里面的TabControl控件没有关闭按钮,而且很难看。
2.有一些已经做好的第三方控件,但是收费。
3.由于我的故障树推理诊断项目在绘图的时候允许同时打开多个文档进行操作,就要实现类似于浏览器的多标签功能,而且要可以关闭。
4.所以自己写一个类继承TabControl类,然后重写一些里面的方法即可实现。
5.特色:有关闭按钮,标签有背景颜色,选中的标签和没选中的颜色不一样,实现鼠标中键和右键的功能
先看我的项目中的完整代码,有很多代码是我的项目需要,可根据你的项目需求删减,核心的代码后面详细解释:
/// <summary> /// 重写的TabControl控件 带关闭按钮 /// </summary> public class MyTabControl : TabControl { private int iconWidth = 16; private int iconHeight = 16; private Image icon = null; private Brush biaocolor = Brushes.Silver; //选项卡的背景色 private Form_paint father;//父窗口,即绘图界面,为的是当选项卡全关后调用父窗口的dispose事件关闭父窗口 private AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl axDrawingControl1; public MyTabControl(AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl axDrawingControl) : base() { this.axDrawingControl1 = axDrawingControl; this.ItemSize = new Size(50, 25); //设置选项卡标签的大小,可改变高不可改变宽 //this.Appearance = TabAppearance.Buttons; //选项卡的显示模式 this.DrawMode = TabDrawMode.OwnerDrawFixed; icon = Properties.Resources.close.ToBitmap(); iconWidth = icon.Width; iconHeight = icon.Height; } /// <summary> /// 设置父窗口 /// </summary> /// <param name="fp">画图窗口</param> public void setFather(Form_paint fp) { this.father = fp; } /// <summary> /// 重写的绘制事件 /// </summary> /// <param name="e"></param> protected override void OnDrawItem(DrawItemEventArgs e)//重写绘制事件。 { Graphics g = e.Graphics; Rectangle r = GetTabRect(e.Index); if (e.Index == this.SelectedIndex) //当前选中的Tab页,设置不同的样式以示选中 { Brush selected_color = Brushes.Gold; //选中的项的背景色 g.FillRectangle(selected_color, r); //改变选项卡标签的背景色 string title = this.TabPages[e.Index].Text + " "; g.DrawString(title, this.Font, new SolidBrush(Color.Black), new PointF(r.X + 3, r.Y + 6));//PointF选项卡标题的位置 r.Offset(r.Width - iconWidth - 3, 2); g.DrawImage(icon, new Point(r.X - 2, r.Y + 2));//选项卡上的图标的位置 fntTab = new System.Drawing.Font(e.Font, FontStyle.Bold); } else//非选中的 { g.FillRectangle(biaocolor, r); //改变选项卡标签的背景色 string title = this.TabPages[e.Index].Text + " "; g.DrawString(title, this.Font, new SolidBrush(Color.Black), new PointF(r.X + 3, r.Y + 6));//PointF选项卡标题的位置 r.Offset(r.Width - iconWidth - 3, 2); g.DrawImage(icon, new Point(r.X - 2, r.Y + 2));//选项卡上的图标的位置 } } protected override void OnMouseClick(MouseEventArgs e) { #region 左键判断是否在关闭区域 if (e.Button == MouseButtons.Left) { Point p = e.Location; Rectangle r = GetTabRect(this.SelectedIndex); r.Offset(r.Width - iconWidth - 3, 2); r.Width = iconWidth; r.Height = iconHeight; if (r.Contains(p)) //点击特定区域时才发生 { string temp = this.SelectedTab.Text; if (temp[temp.Length - 5] == '*')//有变化才保存 { //确认是否保存VSD文档到ft_doc_Path DialogResult response = MessageBox.Show("是否保存故障树" + this.SelectedTab.Name + "到图形文件", "请确认", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (response == System.Windows.Forms.DialogResult.Yes)//确认保存 { axDrawingControl1.Document.SaveAs(GlobalVariables.ft_doc_Path + axDrawingControl1.Document.Title + ".vsd");//保存当前文档到文件夹 string datetime = DateTime.Now.ToString();//获取当前时间 helpTool.saveVsdDB(axDrawingControl1.Document.Title, datetime);//保存vsd文档到数据库 helpTool.setDatetimeToXml(axDrawingControl1.Document.Title, datetime);//如果信息已存在则将xml中的日期更新,如果不存在直接插入 this.SelectedTab.Text = temp.Substring(0, temp.Length - 5) + " ";//保存后取消星号标志,还原为没变化的时候的样式 } else if (response == System.Windows.Forms.DialogResult.Cancel)//点击取消或者关闭 { return;//直接退出,撤销这次关闭程序的事件。 } } if (this.TabCount == 1)//是最后一个选项卡,直接关闭父界面,即画图界面 { father.DisposeForTabControl(true); } else//不是最后一个 { this.TabPages.Remove(this.SelectedTab); } } } #endregion #region 右键 选中 else if (e.Button == MouseButtons.Right) // 右键选中 { for (int i = 0; i < this.TabPages.Count; i++) { TabPage tp = this.TabPages[i]; if (this.GetTabRect(i).Contains(new Point(e.X, e.Y))) { this.SelectedTab = tp; break; } } } #endregion #region 中键 选中 关闭 else if (e.Button == MouseButtons.Middle)//鼠标中键关闭 { for (int i = 0; i < this.TabPages.Count; i++) { TabPage tp = this.TabPages[i]; if (this.GetTabRect(i).Contains(new Point(e.X, e.Y)))//找到后,关闭 { this.SelectedTab = tp; string temp = tp.Text; if (temp[temp.Length - 5] == '*')//有变化才保存 { //确认是否保存VSD文档到ft_doc_Path DialogResult response = MessageBox.Show("是否保存故障树" + this.SelectedTab.Name + "到图形文件", "请确认", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (response == System.Windows.Forms.DialogResult.Yes)//确认保存 { axDrawingControl1.Document.SaveAs(GlobalVariables.ft_doc_Path + axDrawingControl1.Document.Title + ".vsd");//保存当前文档到文件夹 string datetime = DateTime.Now.ToString();//获取当前时间 helpTool.saveVsdDB(axDrawingControl1.Document.Title, datetime);//保存vsd文档到数据库 helpTool.setDatetimeToXml(axDrawingControl1.Document.Title, datetime);//如果信息已存在则将xml中的日期更新,如果不存在直接插入 this.SelectedTab.Text = temp.Substring(0, temp.Length - 5) + " ";//保存后取消星号标志,还原为没变化的时候的样式 } else if (response == System.Windows.Forms.DialogResult.Cancel)//点击取消或者关闭 { return;//直接退出,撤销这次关闭程序的事件。 } } if (this.TabCount == 1)//是最后一个选项卡,直接关闭父界面,即画图界面 { father.DisposeForTabControl(true); } else//不是最后一个 { this.TabPages.Remove(this.SelectedTab); } break; } } } #endregion } }
protected override void OnDrawItem(DrawItemEventArgs e)//重写绘制事件。 { Graphics g = e.Graphics; Rectangle r = GetTabRect(e.Index); if (e.Index == this.SelectedIndex) //当前选中的Tab页,设置不同的样式以示选中 { Brush selected_color = Brushes.Gold; //选中的项的背景色 g.FillRectangle(selected_color, r); //改变选项卡标签的背景色 string title = this.TabPages[e.Index].Text + " "; g.DrawString(title, this.Font, new SolidBrush(Color.Black), new PointF(r.X + 3, r.Y + 6));//PointF选项卡标题的位置 r.Offset(r.Width - iconWidth - 3, 2); g.DrawImage(icon, new Point(r.X - 2, r.Y + 2));//选项卡上的图标的位置 fntTab = new System.Drawing.Font(e.Font, FontStyle.Bold); } else//非选中的 { g.FillRectangle(biaocolor, r); //改变选项卡标签的背景色 string title = this.TabPages[e.Index].Text + " "; g.DrawString(title, this.Font, new SolidBrush(Color.Black), new PointF(r.X + 3, r.Y + 6));//PointF选项卡标题的位置 r.Offset(r.Width - iconWidth - 3, 2); g.DrawImage(icon, new Point(r.X - 2, r.Y + 2));//选项卡上的图标的位置 } }其中的if-else是用来判断当前选项卡是否是选中的,使选中的颜色和未选中的不一样,项目中不需要的可以去除。
具体实现关闭功能的原理是重写protected override void OnMouseClick(MouseEventArgs e)方法,左键单击会触发对应事件,判断单击的区域位置是否在关闭按钮的区域,实现关闭功能。另外有对中键和右键的处理,根据你的项目,修改对应按钮事件下的代码即可。
protected override void OnMouseClick(MouseEventArgs e) { #region 左键判断是否在关闭区域 if (e.Button == MouseButtons.Left) { Point p = e.Location; Rectangle r = GetTabRect(this.SelectedIndex); r.Offset(r.Width - iconWidth - 3, 2); r.Width = iconWidth; r.Height = iconHeight; if (r.Contains(p)) //点击特定区域时才发生 { string temp = this.SelectedTab.Text; if (temp[temp.Length - 5] == '*')//有变化才保存 { //确认是否保存VSD文档到ft_doc_Path DialogResult response = MessageBox.Show("是否保存故障树" + this.SelectedTab.Name + "到图形文件", "请确认", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (response == System.Windows.Forms.DialogResult.Yes)//确认保存 { axDrawingControl1.Document.SaveAs(GlobalVariables.ft_doc_Path + axDrawingControl1.Document.Title + ".vsd");//保存当前文档到文件夹 string datetime = DateTime.Now.ToString();//获取当前时间 helpTool.saveVsdDB(axDrawingControl1.Document.Title, datetime);//保存vsd文档到数据库 helpTool.setDatetimeToXml(axDrawingControl1.Document.Title, datetime);//如果信息已存在则将xml中的日期更新,如果不存在直接插入 this.SelectedTab.Text = temp.Substring(0, temp.Length - 5) + " ";//保存后取消星号标志,还原为没变化的时候的样式 } else if (response == System.Windows.Forms.DialogResult.Cancel)//点击取消或者关闭 { return;//直接退出,撤销这次关闭程序的事件。 } } if (this.TabCount == 1)//是最后一个选项卡,直接关闭父界面,即画图界面 { father.DisposeForTabControl(true); } else//不是最后一个 { this.TabPages.Remove(this.SelectedTab); } } } #endregion #region 右键 选中 else if (e.Button == MouseButtons.Right) // 右键选中 { for (int i = 0; i < this.TabPages.Count; i++) { TabPage tp = this.TabPages[i]; if (this.GetTabRect(i).Contains(new Point(e.X, e.Y))) { this.SelectedTab = tp; break; } } } #endregion #region 中键 选中 关闭 else if (e.Button == MouseButtons.Middle)//鼠标中键关闭 { for (int i = 0; i < this.TabPages.Count; i++) { TabPage tp = this.TabPages[i]; if (this.GetTabRect(i).Contains(new Point(e.X, e.Y)))//找到后,关闭 { this.SelectedTab = tp; string temp = tp.Text; if (temp[temp.Length - 5] == '*')//有变化才保存 { //确认是否保存VSD文档到ft_doc_Path DialogResult response = MessageBox.Show("是否保存故障树" + this.SelectedTab.Name + "到图形文件", "请确认", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (response == System.Windows.Forms.DialogResult.Yes)//确认保存 { axDrawingControl1.Document.SaveAs(GlobalVariables.ft_doc_Path + axDrawingControl1.Document.Title + ".vsd");//保存当前文档到文件夹 string datetime = DateTime.Now.ToString();//获取当前时间 helpTool.saveVsdDB(axDrawingControl1.Document.Title, datetime);//保存vsd文档到数据库 helpTool.setDatetimeToXml(axDrawingControl1.Document.Title, datetime);//如果信息已存在则将xml中的日期更新,如果不存在直接插入 this.SelectedTab.Text = temp.Substring(0, temp.Length - 5) + " ";//保存后取消星号标志,还原为没变化的时候的样式 } else if (response == System.Windows.Forms.DialogResult.Cancel)//点击取消或者关闭 { return;//直接退出,撤销这次关闭程序的事件。 } } if (this.TabCount == 1)//是最后一个选项卡,直接关闭父界面,即画图界面 { father.DisposeForTabControl(true); } else//不是最后一个 { this.TabPages.Remove(this.SelectedTab); } break; } } } #endregion }
在你的窗体上拖一个TabControl,然后打开对应窗体代码文件的.Designer.cs文件里找到private void InitializeComponent()方法,然后找到里面对应的TabControl的定义语句即 this.TabControl =。。。。改成this.TabControl = new MyTabControl();如果想传参,就在前面重写MyTabControl时加入带参的构造函数(我的就带有参数)。
值得一提的是.Designer.cs文件里找到private void InitializeComponent()方法都是程序根据你的可视化界面设计自动生成的,所以每次你在可视化的设计环境下重新编辑了,这里就会重新生成,所以你得手动再次改一下this.TabControl = new MyTabControl();
我的程序效果如下