A little more work involved here than in the previous case, but definately worth it. Add a new UserControl to your project and replace the code with that shown below.
As you can see we have made the BackColor property of the TabControl Visible in the Designer and put a custom background behind Transparent Tabpages. This control has been updated to incorporate a modified version of the SelectedIndexChanging event in code examples by Matt Hinz and Ken Tucker. The project needs a custom designer to get the Design time painting to behave properly, but that's something for a later project. You'll find TabControlEx on my Controls page with all the work already done.
TabControl First Edition
VB net Sample:Imports System.ComponentModel Imports System.Runtime.InteropServices Imports System.Reflection Public Class TabControl Inherits System.Windows.Forms.TabControl #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call setstyle(ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.DoubleBuffer Or _ ControlStyles.ResizeRedraw Or _ ControlStyles.UserPaint, True) End Sub 'UserControl1 overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() components = New System.ComponentModel.Container End Sub #End Region #Region " InterOP " <StructLayout(LayoutKind.Sequential)> _ Private Structure NMHDR Public HWND As Int32 Public idFrom As Int32 Public code As Int32 Public Overloads Function ToString() As String Return String.Format("Hwnd: {0}, ControlID: {1}, Code: {2}", HWND, idFrom, code) End Function End Structure Private Const TCN_FIRST As Int32 = &HFFFFFFFFFFFFFDDA& Private Const TCN_SELCHANGING As Int32 = (TCN_FIRST - 2) Private Const WM_USER As Int32 = &H400& Private Const WM_NOTIFY As Int32 = &H4E& Private Const WM_REFLECT As Int32 = WM_USER + &H1C00& #End Region #Region " BackColor Manipulation " 'As well as exposing the property to the Designer we want it to behave just like any other 'controls BackColor property so we need some clever manipulation. Private m_Backcolor As Color = Color.Empty <Browsable(True), _ Description("The background color used to display text and graphics in a control.")> _ Public Overrides Property BackColor() As Color Get If m_Backcolor.Equals(Color.Empty) Then If Parent Is Nothing Then Return Control.DefaultBackColor Else Return Parent.BackColor End If End If Return m_Backcolor End Get Set(ByVal Value As Color) If m_Backcolor.Equals(Value) Then Return m_Backcolor = Value Invalidate() 'Let the Tabpages know that the backcolor has changed. MyBase.OnBackColorChanged(EventArgs.Empty) End Set End Property Public Function ShouldSerializeBackColor() As Boolean Return Not m_Backcolor.Equals(Color.Empty) End Function Public Overrides Sub ResetBackColor() m_Backcolor = Color.Empty Invalidate() End Sub #End Region Protected Overrides Sub OnParentBackColorChanged(ByVal e As System.EventArgs) MyBase.OnParentBackColorChanged(e) Invalidate() End Sub Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs) MyBase.OnSelectedIndexChanged(e) Invalidate() End Sub Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(e) e.Graphics.Clear(BackColor) Dim r As Rectangle = Me.ClientRectangle If TabCount <= 0 Then Return 'Draw a custom background for Transparent TabPages r = SelectedTab.Bounds Dim sf As New StringFormat sf.Alignment = StringAlignment.Center sf.LineAlignment = StringAlignment.Center Dim DrawFont As New Font(Font.FontFamily, 24, FontStyle.Regular, GraphicsUnit.Pixel) ControlPaint.DrawStringDisabled(e.Graphics, "Micks Ownerdraw TabControl", DrawFont, BackColor, RectangleF.op_Implicit(r), sf) DrawFont.Dispose() 'Draw a border around TabPage r.Inflate(3, 3) Dim tp As TabPage = TabPages(SelectedIndex) Dim PaintBrush As New SolidBrush(tp.BackColor) e.Graphics.FillRectangle(PaintBrush, r) ControlPaint.DrawBorder(e.Graphics, r, PaintBrush.Color, ButtonBorderStyle.Outset) 'Draw the Tabs For index As Integer = 0 To TabCount - 1 tp = TabPages(index) r = GetTabRect(index) Dim bs As ButtonBorderStyle = ButtonBorderStyle.Outset If index = SelectedIndex Then bs = ButtonBorderStyle.Inset PaintBrush.Color = tp.BackColor e.Graphics.FillRectangle(PaintBrush, r) ControlPaint.DrawBorder(e.Graphics, r, PaintBrush.Color, bs) PaintBrush.Color = tp.ForeColor 'Set up rotation for left and right aligned tabs If Alignment = TabAlignment.Left Or Alignment = TabAlignment.Right Then Dim RotateAngle As Single = 90 If Alignment = TabAlignment.Left Then RotateAngle = 270 Dim cp As New PointF(r.Left + (r.Width \ 2), r.Top + (r.Height \ 2)) e.Graphics.TranslateTransform(cp.X, cp.Y) e.Graphics.RotateTransform(RotateAngle) r = New Rectangle(-(r.Height \ 2), -(r.Width \ 2), r.Height, r.Width) End If 'Draw the Tab Text If tp.Enabled Then e.Graphics.DrawString(tp.Text, Font, PaintBrush, RectangleF.op_Implicit(r), sf) Else ControlPaint.DrawStringDisabled(e.Graphics, tp.Text, Font, tp.BackColor, RectangleF.op_Implicit(r), sf) End If e.Graphics.ResetTransform() Next PaintBrush.Dispose() End Sub <Description("Occurs as a tab is being changed.")> _ Public Event SelectedIndexChanging As SelectedTabPageChangeEventHandler Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = (WM_REFLECT + WM_NOTIFY) Then Dim hdr As NMHDR = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(NMHDR)), NMHDR) If hdr.code = TCN_SELCHANGING Then Dim tp As TabPage = TestTab(Me.PointToClient(Cursor.Position)) If Not tp Is Nothing Then Dim e As New TabPageChangeEventArgs(Me.SelectedTab, tp) RaiseEvent SelectedIndexChanging(Me, e) If e.Cancel OrElse tp.Enabled = False Then m.Result = New IntPtr(1) Return End If End If End If End If MyBase.WndProc(m) End Sub Private Function TestTab(ByVal pt As Point) As TabPage For index As Integer = 0 To TabCount - 1 If GetTabRect(index).Contains(pt.X, pt.Y) Then Return TabPages(index) End If Next Return Nothing End Function End Class #Region " EventArgs Class's " Public Class TabPageChangeEventArgs Inherits EventArgs Private _Selected As TabPage Private _PreSelected As TabPage Public Cancel As Boolean = False Public ReadOnly Property CurrentTab() As TabPage Get Return _Selected End Get End Property Public ReadOnly Property NextTab() As TabPage Get Return _PreSelected End Get End Property Public Sub New(ByVal CurrentTab As TabPage, ByVal NextTab As TabPage) _Selected = CurrentTab _PreSelected = NextTab End Sub End Class Public Delegate Sub SelectedTabPageChangeEventHandler(ByVal sender As Object, ByVal e As TabPageChangeEventArgs) #End Region
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Dotnetrix_Samples { /// <summary> /// Summary description for TabControl. /// </summary> public class TabControl : System.Windows.Forms.TabControl { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public TabControl() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // TODO: Add any initialization after the InitializeComponent call SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion #region Interop [StructLayout(LayoutKind.Sequential)] private struct NMHDR { public IntPtr HWND; public uint idFrom; public int code; public override String ToString() { return String.Format("Hwnd: {0}, ControlID: {1}, Code: {2}", HWND, idFrom, code); } } private const int TCN_FIRST = 0 - 550; private const int TCN_SELCHANGING = (TCN_FIRST - 2); private const int WM_USER = 0x400; private const int WM_NOTIFY = 0x4E; private const int WM_REFLECT = WM_USER + 0x1C00; #endregion #region BackColor Manipulation //As well as exposing the property to the Designer we want it to behave just like any other //controls BackColor property so we need some clever manipulation. private Color m_Backcolor = Color.Empty; [Browsable(true),Description("The background color used to display text and graphics in a control.")] public override Color BackColor { get { if (m_Backcolor.Equals(Color.Empty)) { if (Parent == null) return Control.DefaultBackColor; else return Parent.BackColor; } return m_Backcolor; } set { if (m_Backcolor.Equals(value)) return; m_Backcolor = value; Invalidate(); //Let the Tabpages know that the backcolor has changed. base.OnBackColorChanged(EventArgs.Empty); } } public bool ShouldSerializeBackColor() { return !m_Backcolor.Equals(Color.Empty); } public override void ResetBackColor() { m_Backcolor = Color.Empty; Invalidate(); } #endregion protected override void OnParentBackColorChanged(EventArgs e) { base.OnParentBackColorChanged (e); Invalidate(); } protected override void OnSelectedIndexChanged(EventArgs e) { base.OnSelectedIndexChanged (e); Invalidate(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint (e); e.Graphics.Clear(BackColor); Rectangle r = ClientRectangle; if (TabCount <= 0) return; //Draw a custom background for Transparent TabPages r = SelectedTab.Bounds; StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; Font DrawFont = new Font(Font.FontFamily, 24, FontStyle.Regular, GraphicsUnit.Pixel); ControlPaint.DrawStringDisabled(e.Graphics, "Micks Ownerdraw TabControl", DrawFont, BackColor, (RectangleF)r, sf); DrawFont.Dispose(); //Draw a border around TabPage r.Inflate(3, 3); TabPage tp = TabPages[SelectedIndex]; SolidBrush PaintBrush = new SolidBrush(tp.BackColor); e.Graphics.FillRectangle(PaintBrush, r); ControlPaint.DrawBorder(e.Graphics, r, PaintBrush.Color, ButtonBorderStyle.Outset); //Draw the Tabs for (int index = 0; index <= TabCount - 1; index++) { tp = TabPages[index]; r = GetTabRect(index); ButtonBorderStyle bs = ButtonBorderStyle.Outset; if (index == SelectedIndex) bs = ButtonBorderStyle.Inset; PaintBrush.Color = tp.BackColor; e.Graphics.FillRectangle(PaintBrush, r); ControlPaint.DrawBorder(e.Graphics, r, PaintBrush.Color, bs); PaintBrush.Color = tp.ForeColor; //Set up rotation for left and right aligned tabs if (Alignment == TabAlignment.Left || Alignment == TabAlignment.Right) { float RotateAngle = 90; if (Alignment == TabAlignment.Left) RotateAngle = 270; PointF cp = new PointF(r.Left + (r.Width >> 1), r.Top + (r.Height >> 1)); e.Graphics.TranslateTransform(cp.X, cp.Y); e.Graphics.RotateTransform(RotateAngle); r = new Rectangle(-(r.Height >> 1), -(r.Width >> 1), r.Height, r.Width); } //Draw the Tab Text if (tp.Enabled) e.Graphics.DrawString(tp.Text, Font, PaintBrush, (RectangleF)r, sf); else ControlPaint.DrawStringDisabled(e.Graphics, tp.Text, Font, tp.BackColor, (RectangleF)r, sf); e.Graphics.ResetTransform(); } PaintBrush.Dispose(); } [Description("Occurs as a tab is being changed.")] public event SelectedTabPageChangeEventHandler SelectedIndexChanging; protected override void WndProc(ref Message m) { if (m.Msg == (WM_REFLECT + WM_NOTIFY)) { NMHDR hdr = (NMHDR)(Marshal.PtrToStructure(m.LParam, typeof(NMHDR))); if (hdr.code == TCN_SELCHANGING) { TabPage tp = TestTab(PointToClient(Cursor.Position)); if (tp != null) { TabPageChangeEventArgs e = new TabPageChangeEventArgs(SelectedTab, tp); if (SelectedIndexChanging != null) SelectedIndexChanging(this, e); if (e.Cancel || tp.Enabled == false) { m.Result = new IntPtr(1); return; } } } } base.WndProc (ref m); } private TabPage TestTab(Point pt) { for (int index = 0; index <= TabCount - 1; index++) { if (GetTabRect(index).Contains(pt.X, pt.Y)) return TabPages[index]; } return null; } } public class TabPageChangeEventArgs : EventArgs { private TabPage _Selected = null; private TabPage _PreSelected = null; public bool Cancel = false; public TabPage CurrentTab { get { return _Selected; } } public TabPage NextTab { get { return _PreSelected; } } public TabPageChangeEventArgs(TabPage CurrentTab, TabPage NextTab) { _Selected = CurrentTab; _PreSelected = NextTab; } } public delegate void SelectedTabPageChangeEventHandler(Object sender, TabPageChangeEventArgs e); }
TabControl Second Edition
- Download C# and VB.Net Demo Source Code - 181.9 KB
- Download C++ Dll Source Code - 2.03 MB
- Download Compiled CustomTabControl Assembly - 18.33 KB
Painting Your Own Tabs - First Edition
Painting Your Own Tabs - Second Edition
5 comments:
very nice, i like the custom Tab controls, nice work . [bboyseATymail@.com]
Hi there I am so thrilled I found your website, I really
found you by accident, while I was searching on Bing
for something else, Nonetheless I am here now and would just like to say thanks a
lot for a marvelous post and a all round entertaining
blog (I also love the theme/design), I don't have time to go through it all at the minute but I have saved it and also added in your RSS feeds, so when I have time I will be back to read a lot more, Please do keep up the fantastic job.
My website; online graduate certificate programs
Can you make a video about this stuf?
Can you make a video about this stuf?
still not understanding how toi make costum tab controlls
is there no easy way
Post a Comment