C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

タブコントロールに閉じるボタン /ガンダーラ (22/07/05(Tue) 00:03) #100121
Re[1]: タブコントロールに閉じるボタン /くま (22/07/05(Tue) 01:26) #100122
Re[1]: タブコントロールに閉じるボタン /KOZ (22/07/05(Tue) 05:10) #100124
  └ Re[2]: タブコントロールに閉じるボタン /KOZ (22/07/05(Tue) 16:44) #100126
    └ Re[3]: タブコントロールに閉じるボタン /ガンダーラ (22/07/07(Thu) 06:48) #100144
      └ Re[4]: タブコントロールに閉じるボタン /KOZ (22/07/07(Thu) 08:30) #100147 解決済み


親記事 / ▼[ 100122 ] ▼[ 100124 ]
■100121 / 親階層)  タブコントロールに閉じるボタン
□投稿者/ ガンダーラ (1回)-(2022/07/05(Tue) 00:03:28)

分類:[.NET 全般] 

いつも勉強させていただいてます
vb.netでパソコン上で動作するソフトを作っています

タブコントロールに閉じるボタンをつけたいと考えています
いろいろネットで検索してみても、そのもののサンプルコードは見つけられませんでした


良いコードがあれば教えていただけるととても嬉しいのですが
自分で作るしか無いと思っている状況です


タブコントロールのオーナー描画で通常描画の後に✕ボタンアイコンの描画
マウスオーバでタブを取得して、✕ボタンの位置にきたことを判定
マウスオーバーで✕ボタンアイコンの変更
クリックで✕ボタンの上にいれば、閉じるコードの実行


というアルゴリズムで実現できそうな気がしています

でも、ネット上にそのもののサンプルがないということは
なにか、うまくいかない罠がありそうにも思います

vb.netで
タブコントロールに✕をつけることはできるのか教えてください

有料のツールを使うことは想定しておりません



宜しくお願いします。
[ □ Tree ] 返信 編集キー/

▲[ 100121 ] / 返信無し
■100122 / 1階層)  Re[1]: タブコントロールに閉じるボタン
□投稿者/ くま (213回)-(2022/07/05(Tue) 01:26:25)
2022/07/05(Tue) 01:27:40 編集(投稿者)

> タブコントロールに閉じるボタンをつけたいと考えています

それって「ドッキングレイアウト」の事かな?

C#になるけど
https://araramistudio.jimdo.com/2015/09/03/dockpanelsuite%E3%81%A7%E3%83%89%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E3%82%A6%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6/
https://so-zou.jp/software/tech/library/dockpanel/window/dock-content.htm
https://qiita.com/engo01/items/bc480de47bf1124ca18e

とかかな?無料だよ。

※C#→VB.net
http://www.carlosag.net/Tools/CodeTranslator/
を使うと分かりやすいです。
[ 親 100121 / □ Tree ] 返信 編集キー/

▲[ 100121 ] / ▼[ 100126 ]
■100124 / 1階層)  Re[1]: タブコントロールに閉じるボタン
□投稿者/ KOZ (255回)-(2022/07/05(Tue) 05:10:09)
2022/07/05(Tue) 08:50:24 編集(投稿者)

No100121 (ガンダーラ さん) に返信
> でも、ネット上にそのもののサンプルがないということは
> なにか、うまくいかない罠がありそうにも思います

とくに罠なんてありません。
きっちり作りこむのが大変なだけです。
#それが罠なのかもしれません。(^_^;)

描画については、どぼんさんの HP にタブコントロールのサンプルがいくつかあるので参照してください。
https://dobon.net/vb/dotnet/control/index.html

特に
「TabControlを自分でビジュアルスタイルで描画する」
https://dobon.net/vb/dotnet/control/tabsidebug.html#paint

は読んでおいたほうが良いです。


あとはマウスメッセージに合わせ、ボタンをコントロールするわけですが、
TrackMouseEvent API を実行してマウスの軌跡を追跡する必要があります。

「C# TrackMouseEvent」で検索するとサンプルが出てきます。

タブの数が多くなったり、タブコントロールの幅が小さくなると表示されないタブが
出てくるので注意してください。

面倒すぎるのでサンプルを書く気にはなりません。ごめんなさい。

[ 親 100121 / □ Tree ] 返信 編集キー/

▲[ 100124 ] / ▼[ 100144 ]
■100126 / 2階層)  Re[2]: タブコントロールに閉じるボタン
□投稿者/ KOZ (256回)-(2022/07/05(Tue) 16:44:30)
No100124 (KOZ) に返信
> 面倒すぎるのでサンプルを書く気にはなりません。ごめんなさい。

とは書きましたが、Alignment = Top 固定の手抜き実装で書いてみました。
マウスのトラッキングはしていないのですが、意外にスムーズに見えます。

Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class TabControlEx
    Inherits TabControl

    Protected ButtonFont As New Font("Marlett", 6)
    Protected CloseButtonChar As String = "r"

    Protected Overrides Sub Dispose(disposing As Boolean)
        MyBase.Dispose(disposing)
        ButtonFont.Dispose()
    End Sub

    ' Alignment プロパティを隠す
    <Browsable(False)>
    <EditorBrowsable(False)>
    <DesignerSerializationVisibility(False)>
    Public Shadows Property Alignment As TabAlignment

    ' ボタンの枠を求める
    Protected Overridable Function GetTabButtonRect(index As Integer) As Rectangle
        ' 小さいキャプション ボタンのサイズと TabItem の高さでサイズを調整
        Dim tmpSize As Size = SystemInformation.ToolWindowCaptionButtonSize
        Dim itemHeight As Integer = ((ItemSize.Height - 5) \ 2) * 2
        If tmpSize.Height < itemHeight Then
            itemHeight = tmpSize.Height
        End If
        Dim buttonSize As New Size(itemHeight, itemHeight)
        ' 位置を調整
        Dim tabRect As Rectangle = GetTabRect(index)
        Dim buttonLeft As Integer = tabRect.Right - buttonSize.Width - 2
        Dim buttonTop As Integer = (tabRect.Height - buttonSize.Height) \ 2
        ' 選択されていないタブはちょっと小さい
        If index <> SelectedIndex Then
            buttonLeft -= 2
            buttonTop += 2
        End If
        Return New Rectangle(New Point(buttonLeft, buttonTop), buttonSize)
    End Function

    ' ボタンを描画
    Protected Overridable Sub DrawButton(g As Graphics, index As Integer)
        Dim buttonRect As Rectangle = GetTabButtonRect(index)
        g.FillRectangle(Brushes.Red, buttonRect)
        Dim fmt As New StringFormat With {
            .FormatFlags = StringFormatFlags.NoClip,
            .Alignment = StringAlignment.Center,
            .LineAlignment = StringAlignment.Center
        }
        g.DrawString(CloseButtonChar, ButtonFont, Brushes.White, buttonRect, fmt)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        Select Case m.Msg
            Case WM_PAINT
                Using bmp As New Bitmap(ClientSize.Width, ClientSize.Height)
                    Using g As Graphics = Graphics.FromImage(bmp)
                        ' コントロールを Bitmap に描画
                        Dim dc As IntPtr = g.GetHdc()
                        Dim msg As Message = Message.Create(m.HWnd, m.Msg, dc, m.LParam)
                        MyBase.WndProc(msg)
                        g.ReleaseHdc(dc)

                        ' Bitmap にボタンを上書き
                        For index As Integer = 0 To TabCount - 1
                            Dim mouseClientPos As Point = PointToClient(MousePosition)
                            Dim tabRect As Rectangle = GetTabRect(index)
                            ' アクティブな TabPage またはマウス下の TabPage に描画する
                            If index = SelectedIndex OrElse tabRect.Contains(mouseClientPos) Then
                                DrawButton(g, index)
                            End If
                        Next
                    End Using

                    If m.WParam = IntPtr.Zero Then
                        ' コントロールに描画
                        Dim ps As New PAINTSTRUCT()
                        Dim hdc As IntPtr = BeginPaint(m.HWnd, ps)
                        Using g As Graphics = Graphics.FromHdc(hdc)
                            g.DrawImage(bmp, Point.Empty)
                        End Using
                        EndPaint(m.HWnd, ps)
                    Else
                        ' WParam に渡された hdc に描画
                        Using g As Graphics = Graphics.FromHdc(m.WParam)
                            g.DrawImage(bmp, Point.Empty)
                        End Using
                    End If
                End Using
            Case Else
                MyBase.WndProc(m)
        End Select
    End Sub

    Protected Overrides Sub OnClick(e As EventArgs)
        MyBase.OnClick(e)
        ' マウスの位置がボタン上なら、そのタブを消す
        Dim mouseClientPos As Point = PointToClient(MousePosition)
        For index As Integer = 0 To TabCount - 1
            Dim btnRect As Rectangle = GetTabButtonRect(index)
            If btnRect.Contains(mouseClientPos) Then
                TabPages.RemoveAt(index)
                Exit For
            End If
        Next
    End Sub

    Private Const WM_PAINT As Integer = &HF

    <StructLayout(LayoutKind.Sequential)>
    Private Structure RECT
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential, Pack:=4)>
    Private Structure PAINTSTRUCT
        Public hdc As IntPtr
        Public fErase As Integer
        Public rcPaint As RECT
        Public fRestore As Integer
        Public fIncUpdate As Integer
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)>
        Public rgbReserved As Byte()
    End Structure

    <DllImport("user32.dll")>
    Private Shared Function BeginPaint(ByVal hwnd As IntPtr, <Out()> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

    <DllImport("user32.dll")>
    Private Shared Function EndPaint(ByVal hwnd As IntPtr, <[In]()> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

End Class

[ 親 100121 / □ Tree ] 返信 編集キー/

▲[ 100126 ] / ▼[ 100147 ]
■100144 / 3階層)  Re[3]: タブコントロールに閉じるボタン
□投稿者/ ガンダーラ (2回)-(2022/07/07(Thu) 06:48:16)
くまさま

ありがとうございます、希望していたものとは違いましたが
すごく使いやすそうなコントロールですので勉強したいと思います。


KOZさま

すごいサンプルありがとうございます
自分では絶対たどり着けないコードです。

とくにこのあたりはなかなか勉強のしがいがあり、感動です
とにかくやってみます
ありがとうございました



    ' Alignment プロパティを隠す
    <Browsable(False)>
    <EditorBrowsable(False)>
    <DesignerSerializationVisibility(False)>
    Public Shadows Property Alignment As TabAlignment



   <StructLayout(LayoutKind.Sequential, Pack:=4)>
    Private Structure PAINTSTRUCT
        Public hdc As IntPtr
        Public fErase As Integer
        Public rcPaint As RECT
        Public fRestore As Integer
        Public fIncUpdate As Integer
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)>
        Public rgbReserved As Byte()
    End Structure

    <DllImport("user32.dll")>
    Private Shared Function BeginPaint(ByVal hwnd As IntPtr, <Out()> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

    <DllImport("user32.dll")>
    Private Shared Function EndPaint(ByVal hwnd As IntPtr, <[In]()> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

[ 親 100121 / □ Tree ] 返信 編集キー/

▲[ 100144 ] / 返信無し
■100147 / 4階層)  Re[4]: タブコントロールに閉じるボタン
□投稿者/ KOZ (265回)-(2022/07/07(Thu) 08:30:25)
No100144 (ガンダーラ さん) に返信

マウスをトラッキングして再描画手続きしなきゃいけないかなと思いましたが、タブコントロールがやってくれてるみたいです。

> とにかくやってみます
> ありがとうございました

がんばってください。
解決済みにしておきます。
解決済み
[ 親 100121 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -