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
}
}
實現關閉按鈕的關鍵代碼是重寫OnDrawItem(DrawItemEventArgs e)方法:
代碼如下:
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();
我的程序效果如下