TabControl 의 각 TabPage 에 버튼이 필요할 때가 있다.
각 TabPage 에 버튼을 넣을려면
TabControl 의 OnDrawItem 메소드를 override 하여
일일이 헤더 문자열 하고 버튼을 위치 간격 다 계산해서 우리의 Graphics g 로 다 그려줘야 한다.
그리고 찾아보면 은근 소스도 쉽게 찾을 수 있다.
하지만 만약 TabPage 마다 버튼 갯수도 다르고 버튼마다 아이콘도 맘대로 넣고 헤더 문자열도 변경할 수 있고
덤으로 Drag&Drop 으로 순서까지 변경하게 해달라고 하면 약간 골치 아퍼진다.
그래서 이 소스 저 소스를 버무려봤다.
우선 쉽게 버튼에 대한 정보를 저장하고 넘길 때 사용할 아이가 필요하다.
using System.Drawing;
public class BtnTabPageInfo
{
private int mId = -1;
private Image mEnable = null;
private Image mDisable = null;
/// <summary>
/// 버튼을 구분할 ID
/// </summary>
public int Id
{
get { return this.mId; }
}
/// <summary>
/// 활성시 표시될 버튼 이미지
/// </summary>
public Image Enable
{
get { return this.mEnable; }
}
/// <summary>
/// 비활성시 표시될 버튼 이미지
/// </summary>
public Image Disable
{
get { return this.mDisable; }
}
public BtnTabPageInfo(int Id, Image Enable, Image Disable)
{
this.mId = Id;
this.mEnable = Enable;
this.mDisable = Disable;
}
}
그리고 TabPage 를 상속받고
버튼에 대한 정보 및 헤더 문자열에 대한 수정 여부를 저장할 수 있게 하고
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
public partial class BtnTabPage : System.Windows.Forms.TabPage
{
#region Declare Field
private List<BtnTabPageInfo> mItmes = null;
private bool mAllowTabEdit = false;
#endregion
#region Property
/// <summary>
/// TabPage 헤더 수정 허용 여부
/// </summary>
public bool AllowTabEdit
{
get { return this.mAllowTabEdit; }
set { this.mAllowTabEdit = value; }
}
/// <summary>
/// 버튼 갯수
/// </summary>
public int Count
{
get{ return this.mItmes.Count; }
}
/// <summary>
/// 인덱서
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public BtnTabPageInfo this[int index]
{
get{ return this.mItmes[index]; }
}
#endregion
#region Constructor && Destructor
public BtnTabPage()
{
InitializeComponent();
this.mItmes = new List<BtnTabPageInfo>();
}
public BtnTabPage(string Text)
: this()
{
this.Text = Text;
}
public BtnTabPage(System.Windows.Forms.Control Ctrl)
: this(Ctrl.Name, Ctrl)
{ }
public BtnTabPage(string Text, System.Windows.Forms.Control Ctrl)
: this(Text)
{
Ctrl.Dock = DockStyle.Fill;
this.Controls.Add(Ctrl);
}
public BtnTabPage(IContainer container)
{
container.Add(this);
InitializeComponent();
this.mItmes = new List<BtnTabPageInfo>();
}
~BtnTabPage()
{
this.mItmes = null;
}
/// <summary>
/// Designer 의 Dispose 에서 호출
/// </summary>
private void DisposeEx()
{
if (this.mItmes != null)
this.mItmes.Clear();
while (this.Controls.Count > 0)
{
System.Windows.Forms.Control ctrl = this.Controls[0];
ctrl.Dispose();
this.Controls.Remove(ctrl);
}
this.Controls.Clear();
}
#endregion
#region Method
public void AddTabItem(BtnTabPageInfo Item)
{
this.mItmes.Add(Item);
this.Text = GetTabHeader(this.Text);
}
public string GetTabHeader(string HeaderText)
{
string sResult = (HeaderText.TrimEnd() + " ");
if (this.mItmes != null && this.mItmes.Count > 0)
sResult += "".PadRight(6 * this.mItmes.Count, ' ');
return sResult;
}
#endregion
}
그리고 젤 중요한 TabControl 을 상속받아서 대충 이렇게 저렇게
메소드들 재정의해서 잘 그려주고 옮겨주면 된다.
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class BtnTabControl : System.Windows.Forms.TabControl
{
#region Declare Delegate
public delegate void DelTabClicked(object sender, int SelectedTabIndex);
public delegate void DelTabItemClicked(object sender, int SelectedTabIndex, int ButtonID);
#endregion
#region Declare Field
public event DelTabClicked TabRename = null;
public event DelTabClicked TabClick = null;
public event DelTabItemClicked TabItemClicked = null;
private bool mAllowTabMove = false;
#endregion
#region Property
/// <summary>
/// TabPage 이동기능 허용 여부
/// </summary>
public bool AllowTabMove
{
get
{
return this.mAllowTabMove;
}
set
{
this.mAllowTabMove = value;
}
}
#endregion
#region Constructor && Destructor
public BtnTabControl()
{
InitializeComponent();
this.AllowDrop = true;
this.TabStop = false;
this.ItemSize = new System.Drawing.Size(0, 24);
this.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
this.SetStyle(System.Windows.Forms.ControlStyles.DoubleBuffer, true);
}
public BtnTabControl(IContainer container)
: this()
{
container.Add(this);
}
#endregion
#region Event Handler
#endregion
#region Public Method
#endregion
#region Protected Method
/// <summary>
/// TabPage 이동을 위한 부분
/// </summary>
/// <param name="e"></param>
protected override void OnDragOver(System.Windows.Forms.DragEventArgs e)
{
base.OnDragOver(e);
if (this.mAllowTabMove)
{
Point pt = new Point(e.X, e.Y);
//We need client coordinates.
pt = PointToClient(pt);
//Get the tab we are hovering over.
System.Windows.Forms.TabPage hover_tab = GetTabPageByTab(pt);
//Make sure we are on a tab.
if (hover_tab != null)
{
//Make sure there is a TabPage being dragged.
if (e.Data.GetDataPresent(hover_tab.GetType()))
{
e.Effect = DragDropEffects.Move;
System.Windows.Forms.TabPage drag_tab = (System.Windows.Forms.TabPage)e.Data.GetData(hover_tab.GetType());
int item_drag_index = FindIndex(drag_tab);
int drop_location_index = FindIndex(hover_tab);
//Don't do anything if we are hovering over ourself.
if (item_drag_index != drop_location_index)
{
ArrayList pages = new ArrayList();
//Put all tab pages into an array.
for (int i = 0; i < TabPages.Count; i++)
{
//Except the one we are dragging.
if (i != item_drag_index)
pages.Add(TabPages[i]);
}
//Now put the one we are dragging it at the proper location.
pages.Insert(drop_location_index, drag_tab);
//Make them all go away for a nanosec.
TabPages.Clear();
//Add them all back in.
TabPages.AddRange((System.Windows.Forms.TabPage[])pages.ToArray(hover_tab.GetType()));
//Make sure the drag tab is selected.
SelectedTab = null;
SelectedTab = drag_tab;
}
}
}
else
{
e.Effect = DragDropEffects.None;
}
}
}
/// <summary>
/// TabPage Title Menu 를 위한 부분
/// </summary>
/// <param name="e"></param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Bounds != RectangleF.Empty)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF tabTextArea = RectangleF.Empty;
for (int nIndex = 0; nIndex < this.TabCount; nIndex++)
{
tabTextArea = (RectangleF)this.GetTabRect(nIndex);
bool IsActiveTab = false;
if (nIndex != this.SelectedIndex)
IsActiveTab = false;
else
IsActiveTab = true;
//Drawing Tab Panel
DrawTabPanel(tabTextArea, e.Graphics, IsActiveTab);
BtnTabPage tPage = this.TabPages[nIndex] as BtnTabPage;
if (tPage != null)
{
for (int i = 0; i < tPage.Count; i++)
{
Image bImg = (IsActiveTab ? tPage[i].Enable : tPage[i].Disable);
RectangleF tRect = GetButtonRect(tabTextArea, i);
e.Graphics.DrawImage(
bImg,
tRect,
new Rectangle(0, 0, bImg.Width, bImg.Height),
GraphicsUnit.Pixel);
}
}
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Near;
e.Graphics.DrawString(
this.TabPages[nIndex].Text,
this.Font,
new SolidBrush(this.TabPages[nIndex].ForeColor),
new RectangleF(tabTextArea.Left + 4, tabTextArea.Top + 1, tabTextArea.Width, tabTextArea.Height),
sf);
}
}
}
/// <summary>
/// TabPage 이동을 위한 부분
/// TabPage Title Menu 를 위한 부분
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (this.TabClick != null)
{
this.TabClick(this, SelectedIndex);
}
//////////////////////////////////////////////////////////////////////////////
// TabPage 이동을 위한 부분
if (this.mAllowTabMove)
{
Point pt1 = new Point(e.X, e.Y);
System.Windows.Forms.TabPage tp = this.GetTabPageByTab(pt1);
if (tp != null)
base.DoDragDrop(tp, DragDropEffects.All);
}
////////////////////////////////////////////////////////////////////////////////
//// TabPage Header Edit 를 위한 부분
if (e.Clicks > 1) // 더블클릭이면
{
BtnTabPage tPage = this.TabPages[SelectedIndex] as BtnTabPage;
if (tPage.AllowTabEdit)
{
if (tPage != null)
{
string sValue = Microsoft.VisualBasic.Interaction.InputBox(
this.TabPages[SelectedIndex].Text.Trim(), "Input", string.Empty);
if (!string.IsNullOrEmpty(sValue) &&
sValue.CompareTo(this.TabPages[SelectedIndex].Text.Trim()) != 0)
{
tPage.Text = tPage.GetTabHeader(sValue);
if (this.TabRename != null)
{
this.TabRename(this, SelectedIndex);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
// TabPage Title Menu 를 위한 부분
else if (!this.DesignMode)
{
BtnTabPage tTabPage = this.TabPages[SelectedIndex] as BtnTabPage;
if (tTabPage != null)
{
RectangleF tabTextArea = (RectangleF)this.GetTabRect(SelectedIndex);
for (int i = 0; i < tTabPage.Count; i++)
{
RectangleF RectButton = GetButtonRect(tabTextArea, i);
if (RectButton.Contains(new Point(e.X, e.Y)))
{
if (this.TabItemClicked != null)
this.TabItemClicked(this, SelectedIndex, tTabPage[i].Id);
}
}
}
}
}
#endregion
#region Private Method
/// <summary>
/// TabPage 이동을 위한 부분
/// </summary>
private System.Windows.Forms.TabPage GetTabPageByTab(Point pt)
{
System.Windows.Forms.TabPage tp = null;
for (int i = 0; i < TabPages.Count; i++)
{
if (GetTabRect(i).Contains(pt))
{
tp = TabPages[i];
break;
}
}
return tp;
}
/// <summary>
/// TabPage 이동을 위한 부분
/// </summary>
private int FindIndex(System.Windows.Forms.TabPage page)
{
for (int i = 0; i < TabPages.Count; i++)
{
if (TabPages[i] == page)
return i;
}
return -1;
}
private RectangleF GetButtonRect(RectangleF RectTabHeader, int PosIndex)
{
RectangleF RectButton;
switch (PosIndex)
{
case -1: // TabPage Header 부분
RectButton = new RectangleF(
RectTabHeader.X + 1,
RectTabHeader.Y + 1,
RectTabHeader.X + RectTabHeader.Width - 45,
RectTabHeader.Height - 2);
break;
default: // Button 부분
PosIndex++;
RectButton = new RectangleF(
RectTabHeader.X + RectTabHeader.Width - ((22 * PosIndex) + (PosIndex - 1)) + 1,
4 + 1,
RectTabHeader.Height - 4 - 2,
RectTabHeader.Height - 4 - 2);
break;
}
return RectButton;
}
private void DrawTabPanel(RectangleF recTextArea, Graphics g, bool IsEnable)
{
using (LinearGradientBrush _Brush = new LinearGradientBrush(recTextArea, SystemColors.Control, SystemColors.ControlLight, LinearGradientMode.Vertical))
{
GraphicsPath _Path = new GraphicsPath();
_Path.AddRectangle(recTextArea);
ColorBlend _ColorBlend = new ColorBlend(3);
if(IsEnable)
{
_ColorBlend.Colors = new Color[]{SystemColors.ControlLightLight,
Color.FromArgb(255,SystemColors.Control),SystemColors.ControlLight,
SystemColors.Control};
}
else
{
_ColorBlend.Colors = new Color[]{SystemColors.ControlLightLight,
Color.FromArgb(255,SystemColors.ControlLight),SystemColors.ControlDark,
SystemColors.ControlLightLight};
}
_ColorBlend.Positions = new float[] { 0f, .4f, 0.5f, 1f };
_Brush.InterpolationColors = _ColorBlend;
g.FillPath(_Brush, _Path);
using (Pen pen = new Pen(SystemColors.ActiveBorder))
{
g.DrawPath(pen, _Path);
}
_Path.Dispose();
}
}
#endregion
}
그리고 대충 요런식으로 사용해주면
BtnTabControl btnTabControl1;
...
this.btnTabControl1.AllowTabMove = true; // TabMove 기능 활성화
this.btnTabControl1.TabItemClicked += btnTabControl1_TabItemClicked;
this.btnTabControl1.TabRename += btnTabControl1_TabRename;
BtnTabPage tTabPage1 = new BtnTabPage();
tTabPage1.Text = "TabPage1";
tTabPage1.AllowTabEdit = true; // TabPage Header 편집기능 활성화
tTabPage1.Controls.Add(new TextBox { Dock = DockStyle.Fill, Multiline = true });
tTabPage1.AddTabItem(new BtnTabPageInfo(0, Properties.Resources.Navigation_Next_32x32_Regular, Properties.Resources.Navigation_Next_32x32_Disabled));
tTabPage1.AddTabItem(new BtnTabPageInfo(1, Properties.Resources.Navigation_Previous_32x32_Regular, Properties.Resources.Navigation_Previous_32x32_Disabled));
BtnTabPage tTabPage2 = new BtnTabPage();
tTabPage2.Text = "TabPage2";
tTabPage2.AllowTabEdit = true; // TabPage Header 편집기능 활성화
tTabPage2.Controls.Add(new TextBox { Dock = DockStyle.Fill, Multiline = true });
tTabPage2.AddTabItem(new BtnTabPageInfo(0, Properties.Resources.Navigation_Next_32x32_Regular, Properties.Resources.Navigation_Next_32x32_Disabled));
tTabPage2.AddTabItem(new BtnTabPageInfo(1, Properties.Resources.Navigation_Previous_32x32_Regular, Properties.Resources.Navigation_Previous_32x32_Disabled));
btnTabControl1.TabPages.Add(tTabPage1);
btnTabControl1.TabPages.Add(tTabPage2);
....
void btnTabControl1_TabRename(object sender, int SelectedTabIndex)
{
MessageBox.Show("헤더 변경");
}
void btnTabControl1_TabItemClicked(object sender, int SelectedTabIndex, int ButtonID)
{
MessageBox.Show("버튼 클릭 : ID " + ButtonID.ToString());
}
이런식으로 버튼이 나오게 되고 버튼을 누르면 설정한 ID 를 통해서 기능을 분기할 수 있다.
'Dev::DotNet > WinForm' 카테고리의 다른 글
Spread 에서 이미지 Attach 시 Memory Leak (0) | 2013.11.27 |
---|---|
Spread 의 Annotation 시 속도 문제 (2) | 2013.11.20 |
FarPoint Spread 출력화면 이미지 추출 (0) | 2013.10.23 |
ProeprtyGrid 에서 사용자 Editor 생성 (0) | 2013.02.12 |
Property 속성 변경 (0) | 2013.02.05 |