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