ビジュアルスタイルが有効になっており、タブコントロールの外観が通常(TabControl.AppearanceプロパティがTabAppearance.Normal)のとき、タブを右または左に表示しようとすると(TabControl.AlignmentプロパティをTabAlignment.LeftまたはTabAlignment.Rightにすると)、テキストが表示されなくなります。また、タブを下に表示したとき(TabControl.AlignmentプロパティをTabAlignment.Bottomにしたとき)は、テキストは表示されますが、タブの向きが上下逆になります。これを回避するには、ビジュアルスタイルを無効にするか、タブを自分で描画します。ここではこれらの方法を説明します。
アプリケーションのビジュアルスタイルを無効にする方法は、「コントロールの外観をビジュアルスタイル(XPスタイル)にする」で説明しています。しかしこの方法では、すべてのコントロールのビジュアルスタイルが無効になってしまいます。
あるコントロールだけビジュアルスタイルを無効にするには、Win32 APIのSetWindowTheme関数を使います。
以下に、TabControl1のビジュアルスタイルを無効にする例を示します。
<System.Runtime.InteropServices.DllImportAttribute("uxtheme.dll")> _ Private Shared Function SetWindowTheme( _ ByVal hwnd As IntPtr, _ ByVal subAppName As String, _ ByVal subIdList As String) As Integer End Function 'フォームのLoadイベントハンドラ Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load 'TabControl1のVisual Styleを無効にする SetWindowTheme(TabControl1.Handle, "", "") End Sub
[System.Runtime.InteropServices.DllImportAttribute("uxtheme.dll")] private static extern int SetWindowTheme( IntPtr hwnd, string subAppName, string subIdList); //フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //TabControl1のVisual Styleを無効にする SetWindowTheme(TabControl1.Handle, "", ""); }
この方法を使って、TabControlクラスの派生クラスとして、ビジュアルスタイルが無効になったタブコントロールを作成すると、次のようになります。なおこのクラスの使い方が分からないという方は、こちらをご覧ください。
Imports System.Windows.Forms ''' <summary> ''' ビジュアルスタイルが無効のTabControl ''' </summary> Public Class NoVisualTabControl Inherits TabControl <System.Runtime.InteropServices.DllImportAttribute("uxtheme.dll")> _ Private Shared Function SetWindowTheme( _ ByVal hwnd As IntPtr, _ ByVal subAppName As String, _ ByVal subIdList As String) As Integer End Function Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs) SetWindowTheme(Me.Handle, "", "") MyBase.OnHandleCreated(e) End Sub End Class
using System; using System.Windows.Forms; /// <summary> /// ビジュアルスタイルが無効のTabControl /// </summary> public class NoVisualTabControl : TabControl { [System.Runtime.InteropServices.DllImportAttribute("uxtheme.dll")] private static extern int SetWindowTheme( IntPtr hwnd, string subAppName, string subIdList); protected override void OnHandleCreated(EventArgs e) { SetWindowTheme(this.Handle, "", ""); base.OnHandleCreated(e); } }
次に、TabControlを自分で描画して、ビジュアルスタイルっぽくする方法を考えてみます。TabControlをオーナードローする方法は「TabControlのタブを自分で描画する」で、タブをビジュアルスタイルで描画する方法は「Visualスタイルでコントロールを描画する」で紹介していますので、両者を使えばできそうですが、実際にはそう簡単ではありません。DrawItemイベントでタブを描画する方法ではタブの枠を描画できませんので、TabRenderer.DrawTabItemメソッドでビジュアルスタイルのタブを描画してもおかしくなってしまいます。
そこで、DrawItemイベントではなく、PaintイベントでTabControl全体を描画することにします。TabControlでは通常Paintイベントが発生しませんので、ControlStyles.UserPaintをTrueにする必要があります。
このような方法でビジュアルスタイルのタブを描画する例を示します。このクラスはTabControlから派生しています。OnPaintメソッドでは、TabRenderer.DrawTabItemメソッドで描画した画像をタブの向きに合わせて回転させて表示しています。細かい位置の修正なども行っていますが(試行錯誤で行ないました)、詳しくはコメントをご覧ください。AppearanceプロパティがTabAppearance.Normal以外でもタブを描画したり、ImageListプロパティを無視して画像を表示しなかったりと手抜きも多いですが、あくまでサンプルということで、大目に見てください。
なおこのクラスの使い方が分からないという方は、こちらをご覧ください。
Imports System.Drawing Imports System.Windows.Forms ''' <summary> ''' タブが横や下にあっても正常に表示するTabControl ''' </summary> Public Class VisualTabControl Inherits TabControl Public Sub New() MyBase.New() 'Paintイベントで描画できるようにする Me.SetStyle(ControlStyles.UserPaint, True) 'ダブルバッファリングを有効にする Me.DoubleBuffered = True 'Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True) 'Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True) ''Me.SetStyle(ControlStyles.DoubleBuffer, True) 'リサイズで再描画する Me.ResizeRedraw = True 'Me.SetStyle(ControlStyles.ResizeRedraw, True) 'ControlStyles.UserPaintをTrueすると、 'SizeModeは強制的にTabSizeMode.Fixedにされる Me.SizeMode = TabSizeMode.Fixed Me.ItemSize = New Size(80, 18) Me.Appearance = TabAppearance.Normal Me.Multiline = True End Sub Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) MyBase.OnPaint(e) 'TabControlの背景を塗る e.Graphics.FillRectangle(SystemBrushes.Control, Me.ClientRectangle) If Me.TabPages.Count = 0 Then Return End If 'TabPageの枠を描画する Dim page As TabPage = Me.TabPages(Me.SelectedIndex) Dim pageRect As New Rectangle(page.Bounds.X - 2, _ page.Bounds.Y - 2, _ page.Bounds.Width + 5, _ page.Bounds.Height + 5) TabRenderer.DrawTabPage(e.Graphics, pageRect) 'タブを描画する For i As Integer = 0 To Me.TabPages.Count - 1 page = Me.TabPages(i) Dim tabRect As Rectangle = Me.GetTabRect(i) '表示するタブの状態を決定する Dim state As System.Windows.Forms.VisualStyles.TabItemState If Not Me.Enabled Then state = System.Windows.Forms.VisualStyles.TabItemState.Disabled ElseIf Me.SelectedIndex = i Then state = System.Windows.Forms.VisualStyles.TabItemState.Selected Else state = System.Windows.Forms.VisualStyles.TabItemState.Normal End If '選択されたタブとページの間の境界線を消すために、 '描画する範囲を大きくする If Me.SelectedIndex = i Then If Me.Alignment = TabAlignment.Top Then tabRect.Height += 1 ElseIf Me.Alignment = TabAlignment.Bottom Then tabRect.Y -= 2 tabRect.Height += 2 ElseIf Me.Alignment = TabAlignment.Left Then tabRect.Width += 1 ElseIf Me.Alignment = TabAlignment.Right Then tabRect.X -= 2 tabRect.Width += 2 End If End If '画像のサイズを決定する Dim imgSize As Size If Me.Alignment = TabAlignment.Left OrElse _ Me.Alignment = TabAlignment.Right Then imgSize = New Size(tabRect.Height, tabRect.Width) Else imgSize = tabRect.Size End If 'Bottomの時はTextを表示しない(Textを回転させないため) Dim tabText As String = page.Text If Me.Alignment = TabAlignment.Bottom Then tabText = "" End If 'タブの画像を作成する Dim bmp As New Bitmap(imgSize.Width, imgSize.Height) Dim g As Graphics = Graphics.FromImage(bmp) '高さに1足しているのは、下にできる空白部分を消すため TabRenderer.DrawTabItem(g, _ New Rectangle(0, 0, bmp.Width, bmp.Height + 1), _ tabText, _ page.Font, _ False, _ state) g.Dispose() '画像を回転する If Me.Alignment = TabAlignment.Bottom Then bmp.RotateFlip(RotateFlipType.Rotate180FlipNone) ElseIf Me.Alignment = TabAlignment.Left Then bmp.RotateFlip(RotateFlipType.Rotate270FlipNone) ElseIf Me.Alignment = TabAlignment.Right Then bmp.RotateFlip(RotateFlipType.Rotate90FlipNone) End If 'Bottomの時はTextを描画する If Me.Alignment = TabAlignment.Bottom Then Dim sf As New StringFormat() sf.Alignment = StringAlignment.Center sf.LineAlignment = StringAlignment.Center g = Graphics.FromImage(bmp) g.DrawString(page.Text, _ page.Font, _ SystemBrushes.ControlText, _ New RectangleF(0, 0, bmp.Width, bmp.Height), _ sf) g.Dispose() sf.Dispose() End If '画像を描画する e.Graphics.DrawImage(bmp, tabRect.X, tabRect.Y, bmp.Width, bmp.Height) bmp.Dispose() Next End Sub End Class
using System; using System.Drawing; using System.Windows.Forms; /// <summary> /// タブが横や下にあっても正常に表示するTabControl /// </summary> public class VisualTabControl : TabControl { public VisualTabControl() : base() { //Paintイベントで描画できるようにする this.SetStyle(ControlStyles.UserPaint, true); //ダブルバッファリングを有効にする this.DoubleBuffered = true; //this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); //this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); ////this.SetStyle(ControlStyles.DoubleBuffer, true); //リサイズで再描画する this.ResizeRedraw = true; //this.SetStyle(ControlStyles.ResizeRedraw, true); //ControlStyles.UserPaintをTrueすると、 //SizeModeは強制的にTabSizeMode.Fixedにされる this.SizeMode = TabSizeMode.Fixed; this.ItemSize = new Size(80, 18); this.Appearance = TabAppearance.Normal; this.Multiline = true; } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //TabControlの背景を塗る e.Graphics.FillRectangle(SystemBrushes.Control, this.ClientRectangle); if (this.TabPages.Count == 0) return; //TabPageの枠を描画する TabPage page = this.TabPages[this.SelectedIndex]; Rectangle pageRect = new Rectangle( page.Bounds.X - 2, page.Bounds.Y - 2, page.Bounds.Width + 5, page.Bounds.Height + 5); TabRenderer.DrawTabPage(e.Graphics, pageRect); //タブを描画する for (int i = 0; i < this.TabPages.Count; i++) { page = this.TabPages[i]; Rectangle tabRect = this.GetTabRect(i); //表示するタブの状態を決定する System.Windows.Forms.VisualStyles.TabItemState state; if (!this.Enabled) { state = System.Windows.Forms.VisualStyles.TabItemState.Disabled; } else if (this.SelectedIndex == i) { state = System.Windows.Forms.VisualStyles.TabItemState.Selected; } else { state = System.Windows.Forms.VisualStyles.TabItemState.Normal; } //選択されたタブとページの間の境界線を消すために、 //描画する範囲を大きくする if (this.SelectedIndex == i) { if (this.Alignment == TabAlignment.Top) { tabRect.Height += 1; } else if (this.Alignment == TabAlignment.Bottom) { tabRect.Y -= 2; tabRect.Height += 2; } else if (this.Alignment == TabAlignment.Left) { tabRect.Width += 1; } else if (this.Alignment == TabAlignment.Right) { tabRect.X -= 2; tabRect.Width += 2; } } //画像のサイズを決定する Size imgSize; if (this.Alignment == TabAlignment.Left || this.Alignment == TabAlignment.Right) { imgSize = new Size(tabRect.Height, tabRect.Width); } else { imgSize = tabRect.Size; } //Bottomの時はTextを表示しない(Textを回転させないため) string tabText = page.Text; if (this.Alignment == TabAlignment.Bottom) { tabText = ""; } //タブの画像を作成する Bitmap bmp = new Bitmap(imgSize.Width, imgSize.Height); Graphics g = Graphics.FromImage(bmp); //高さに1足しているのは、下にできる空白部分を消すため TabRenderer.DrawTabItem(g, new Rectangle(0, 0, bmp.Width, bmp.Height + 1), tabText, page.Font, false, state); g.Dispose(); //画像を回転する if (this.Alignment == TabAlignment.Bottom) { bmp.RotateFlip(RotateFlipType.Rotate180FlipNone); } else if (this.Alignment == TabAlignment.Left) { bmp.RotateFlip(RotateFlipType.Rotate270FlipNone); } else if (this.Alignment == TabAlignment.Right) { bmp.RotateFlip(RotateFlipType.Rotate90FlipNone); } //Bottomの時はTextを描画する if (this.Alignment == TabAlignment.Bottom) { StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g = Graphics.FromImage(bmp); g.DrawString(page.Text, page.Font, SystemBrushes.ControlText, new RectangleF(0, 0, bmp.Width, bmp.Height), sf); g.Dispose(); sf.Dispose(); } //画像を描画する e.Graphics.DrawImage(bmp, tabRect.X, tabRect.Y, bmp.Width, bmp.Height); bmp.Dispose(); } } }
ビジュアルスタイルを無効にしたくはないし、自分で描画するのも面倒だという場合は、サードパーティ製のタブコントロールを使用するという選択肢もあります。以下に無料で使えるタブコントロールを紹介します。