본문 바로가기

Dev::DotNet/WinForm

TabControl - Button / Tab Move


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

        {

            getreturn 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 를 통해서 기능을 분기할 수 있다.