DOBON.NET DOBON.NETプログラミング掲示板過去ログ

タブコントロールのタブの選択について

環境/言語:[OS : Windows 2000 Professional / 言語 : Visual Basic .NET / .NET Framework : 1.1]
分類:[.NET]

【解決したい問題】

タブコントロールについて質問です。
3つのタブをつくり処理を切り替えているのですが、
3つ目のタブを選択したとき2番目のタブを選択できないように
したいのですが、できません。

以前 VBでSSTab コントロールを使用していたときは、
SSTab1.TabEnable(1) = False
で使用できなくすることができました。

今回も同じように、
TabControl1.TabPages(1).Enable = False
としても変化なく選択できてしまいます。

選択できないようにすることは可能なのでしょうか?
動きだけならなんとかこれで・・・。

'前回選択したタブ
Private TabPage As Integer

Private Sub TabControl1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
'前回選択したタブが2の時1のタブには移動不可能
If TabPage = 2 Then
If TabControl1.SelectedIndex = 1 Then
TabControl1.SelectedIndex = TabPage
End If
End If
TabPage = TabControl1.SelectedIndex
End Sub
■No8364に返信(ぢゅすさんの記事)
> 動きだけならなんとかこれで・・・。
>
> '前回選択したタブ
> Private TabPage As Integer
>
> Private Sub TabControl1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
> '前回選択したタブが2の時1のタブには移動不可能
> If TabPage = 2 Then
> If TabControl1.SelectedIndex = 1 Then
> TabControl1.SelectedIndex = TabPage
> End If
> End If
> TabPage = TabControl1.SelectedIndex
> End Sub
>

いつもお世話になっています。
りきです。

興味で、聞かせてください。

ぢゅずさんの方法で実現可能ですが、
SelectedIndexChanged内で、SelectedIndexを操作しているので
一瞬ちらつきます。

なので、ProcessCmdKeyで選択Tab移動前に制御をいれました。
キー(←や→キー)での制御は要望通りに行くのですが
マウスを使った場合が、どうすればよいかわかりません。

OnMouseUpとかで、選択されたTabControlのTabPageを座標から割り出して、
条件に一致(移動させたくなかったら)したら処理を中断する。
ような処理をするしかないのでしょうか?

つまり、SelectedIndexChangedより前に、マウスでの制御を入れるためには
どうしたらよいのでしょうか?

-----------------------
Private WM_KEYDOWN As Integer = &H100
Dim canNotSelectTabIndex As Integer

Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.Msg = Me.WM_KEYDOWN Then
If keyData = Keys.Left Then
'移動できないTabIndex
If Me.canNotSelectTabIndex = Me.SelectedIndex Then
Return True
End If
End If
End If
Return False
End Function
こんにちは、じゃんぬねっと です。
申し訳ありませんが、横槍しちゃいます。

■No8381に返信(rikiさんの記事)
>     If msg.Msg = Me.WM_KEYDOWN Then
>         If keyData = Keys.Left Then
>             '移動できないTabIndex
>             If Me.canNotSelectTabIndex = Me.SelectedIndex Then
>                 Return True
>             End If
>         End If
>     End If
>     Return False

このコードですと、TabPage 内に属する子コントロールにフォーカスがある場合でも実行されちゃいますよね?
現在 Active なコントロールが、この TabControl であるという条件を入れないと (^-^;)

> OnMouseUpとかで、選択されたTabControlのTabPageを座標から割り出して

TabPage が決定されるのは、MouseUp ではないですね。
ちなみに、OnMouseDown だろうと OnMouseUp だとうと、
タイミングとしては既に遅いのです。(^-^;)
■No8382に返信(じゃんぬねっとさんの記事)
> こんにちは、じゃんぬねっと です。
> 申し訳ありませんが、横槍しちゃいます。
>
> ■No8381に返信(rikiさんの記事)
>> If msg.Msg = Me.WM_KEYDOWN Then
>> If keyData = Keys.Left Then
>> '移動できないTabIndex
>> If Me.canNotSelectTabIndex = Me.SelectedIndex Then
>> Return True
>> End If
>> End If
>> End If
>> Return False
>
> このコードですと、TabPage 内に属する子コントロールにフォーカスがある場合でも実行されちゃいますよね?
> 現在 Active なコントロールが、この TabControl であるという条件を入れないと (^-^;)

あっ!本当ですね。すいません・・・(´・ω・`)ショボーン

>
>>OnMouseUpとかで、選択されたTabControlのTabPageを座標から割り出して
>
> TabPage が決定されるのは、MouseUp ではないですね。
> ちなみに、OnMouseDown だろうと OnMouseUp だとうと、
> タイミングとしては既に遅いのです。(^-^;)

となると、WinProcとかPreProcessMessageで判断するのでしょうか?(ピンときませんが・・・)
こんにちは、じゃんぬねっと です。

■No8383に返信(rikiさんの記事)
> あっ!本当ですね。すいません・・・(´・ω・`)ショボーン
嗚呼、気になさらないでくださいね。(^-^*)
ちなみに、Enabled プロパティを利用して、汎用性を高めるとこんなメソッドになりそうですね。

Source:--------------------------------------------------------------------

Private _iSelectedIndex As Integer = 0

Protected Overrides Function ProcessCmdKey(ByRef m As Message, ByVal keyData As Keys) As Boolean
    Dim iTarget As Integer
    Dim hParent As Form

    Select Case keyData
        Case Keys.Left
            iTarget = Me.SelectedIndex - 1

            If iTarget >= 0 Then
                If IsCancelForDisable(iTarget) = True Then
                    Return True
                End If
            End If
        Case Keys.Right
            iTarget = Me.SelectedIndex + 1

            If iTarget <= Me.TabPages.Count - 1 Then
                If IsCancelForDisable(iTarget) = True Then
                    Return True
                End If
            End If
    End Select

    Return MyBase.ProcessCmdKey(m, keyData)
End Function

---------------------------------------------------------------------------

Private Function IsCancelForDisable(ByVal iTarget As Integer) As Boolean
    Dim hParentForm As Form = Me.FindForm()

    If hParentForm Is Nothing = False Then
        If hParentForm.ActiveControl Is Me Then
            Return Not Me.TabPages(iTarget).Enabled
        End If
    End If
End Function

---------------------------------------------------------------------------

> となると、WinProcとかPreProcessMessageで判断するのでしょうか?
> (ピンときませんが・・・)

s/WinProc/WndProc (^-^;)

ただ、WM_LBUTTON のメッセージをキャッチして、
GetChildAtPoint() メソッドを使っても、TabPage ではなく、
TabControl 自体が返ってきてしまいますのでご注意くださいね。



■No8361に返信(Snowさんの記事)
> タブを選択できないようにしたいのですが、できません。

そもそも、TabPage を無効にすればページ内すべてが無効になりますから、
わざわざ移動までさせなくする意味がわかりません。
ユーザーのアクセシビティも考えると、見ることはできた方が良いかと。

ですから、「Tabbed Dialog」より「TabControl」の方が、ユーザーフレンドリですね。



 /***************************************************
  * @Remarks     じゃんぬねっと
  * @Homepage    http://f57.aaa.livedoor.jp/~jeanne/
  * @Blog        http://www.ailight.jp/blog/jeanne/
  ***************************************************/
■No8390に返信(じゃんぬねっとさんの記事)
> こんにちは、じゃんぬねっと です。
>
> ■No8383に返信(rikiさんの記事)
>>あっ!本当ですね。すいません・・・(´・ω・`)ショボーン
> 嗚呼、気になさらないでくださいね。(^-^*)
> ちなみに、Enabled プロパティを利用して、汎用性を高めるとこんなメソッドになりそうですね。
>
> Source:--------------------------------------------------------------------
>
> Private _iSelectedIndex As Integer = 0
>
> Protected Overrides Function ProcessCmdKey(ByRef m As Message, ByVal keyData As Keys) As Boolean
> Dim iTarget As Integer
> Dim hParent As Form
>
> Select Case keyData
> Case Keys.Left
> iTarget = Me.SelectedIndex - 1
>
> If iTarget >= 0 Then
> If IsCancelForDisable(iTarget) = True Then
> Return True
> End If
> End If
> Case Keys.Right
> iTarget = Me.SelectedIndex + 1
>
> If iTarget <= Me.TabPages.Count - 1 Then
> If IsCancelForDisable(iTarget) = True Then
> Return True
> End If
> End If
> End Select
>
> Return MyBase.ProcessCmdKey(m, keyData)
> End Function
>
> ---------------------------------------------------------------------------
>
> Private Function IsCancelForDisable(ByVal iTarget As Integer) As Boolean
> Dim hParentForm As Form = Me.FindForm()
>
> If hParentForm Is Nothing = False Then
> If hParentForm.ActiveControl Is Me Then
> Return Not Me.TabPages(iTarget).Enabled
> End If
> End If
> End Function
>
> ---------------------------------------------------------------------------
>
>>となると、WinProcとかPreProcessMessageで判断するのでしょうか?
>>(ピンときませんが・・・)
>
> s/WinProc/WndProc (^-^;)
>
> ただ、WM_LBUTTON のメッセージをキャッチして、
> GetChildAtPoint() メソッドを使っても、TabPage ではなく、
> TabControl 自体が返ってきてしまいますのでご注意くださいね。
>
>
>
> ■No8361に返信(Snowさんの記事)
>>タブを選択できないようにしたいのですが、できません。
>
> そもそも、TabPage を無効にすればページ内すべてが無効になりますから、
> わざわざ移動までさせなくする意味がわかりません。
> ユーザーのアクセシビティも考えると、見ることはできた方が良いかと。
>
> ですから、「Tabbed Dialog」より「TabControl」の方が、ユーザーフレンドリですね。
>
>
>
>  /***************************************************
>   * @Remarks じゃんぬねっと
>   * @Homepage http://f57.aaa.livedoor.jp/~jeanne/
>   * @Blog http://www.ailight.jp/blog/jeanne/
>   ***************************************************/

じゃぬねっとさんありがとうございます。
もとは、Snowさんの疑問なのに懇切丁寧に教えていただいて
感謝です。
胸のモヤモヤが取れた感じです。
私が「解決済み!」にしたいくらいですよ!
■No8383に返信(rikiさんの記事)

こんにちは
平ちゃんです。
いつもお世話になります。


> となると、WinProcとかPreProcessMessageで判断するのでしょうか?(ピンときませんが・・・)


GetTabRect メソッドをみつけました。
これをフォームのLoad イベントで次のように取得して

Friend Shared TabRect As Rectangle
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
TabRect = Me.TabControl1.GetTabRect(1)
End Sub

これをWinProcで使用すればマウスでPage2を開く事をさせなくできます。

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

Dim Tbpract As Rectangle = Form1.TabRect '

''マウスの左ボタンが押されたときに送られる()
Dim WM_LBUTTONDOWN As Integer = &H201

If m.Msg = WM_LBUTTONDOWN Then
Dim LParam As Integer = m.LParam.ToInt32
Dim mx As Integer = LParam Mod &H10000
Dim my As Integer = LParam \ &H10000

If (Tbpract.Left <= mx And mx <= Tbpract.Left + Tbpract.Width) And (Tbpract.Top <= my And my <= Tbpract.Top + Tbpract.Height) Then

Exit Sub

End If
End If
MyBase.WndProc(m)
End Sub

参考にして下さい。
#ちょっと今更かなぁと思いつつ(汗)

 私もじゃんぬねっとさんが No8390 で仰った
> そもそも、TabPage を無効にすればページ内すべてが無効になりますから、
> わざわざ移動までさせなくする意味がわかりません。
と同意見です。移動させたくないなら非表示にしてしまったほうが良いのでは?とも思います。
‥‥が、それはそれとして。


> キー(←や→キー)での制御は要望通りに行くのですが
> マウスを使った場合が、どうすればよいかわかりません。

 タブの切り替えは Ctrl ( + Shift ) + Tab キーでも行えますので、もし実装するのであれば
その備えも必要になるでしょう。また、 SelectedIndex もしくは SelectedTab を直接変更したときの
ことも考慮しておいたほうが良いかも知れません。
<そういうコーディングをしないのが確実であれば不要でしょうけど。



 で、言いっぱなしもなんなので TabControl を継承したクラスを作成するという方向から考えてみました。
TabPage の Enabled が False になっているタブは選択できなくなっていると思います。
 改善の余地は多分にありますが参考まで。


Public Class TabControlCustom
    Inherits System.Windows.Forms.TabControl

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Dim index As Integer

        Select Case m.Msg
            Case &H130C 'TCM_SETCURSEL
                If Not Me.TabPages(m.WParam.ToInt32()).Enabled Then Return

            Case &H100 'WM_KEYDOWN
                index = Me.SelectedIndex
                If m.WParam.ToInt32() = &H25 Then 'VK_LEFT
                    index -= 1
                    If index < 0 Then Exit Select
                ElseIf m.WParam.ToInt32() = &H27 Then 'VK_RIGHT
                    index += 1
                    If index >= Me.TabCount Then Exit Select
                Else
                    Exit Select
                End If
                If Not Me.TabPages(index).Enabled Then Return

            Case &H84 'WM_NCHITTEST
                Dim p As Point = Me.PointToClient(New Point(m.LParam.ToInt32()))
                For index = 0 To Me.TabCount - 1
                    If Me.GetTabRect(index).Contains(p) Then
                        If Me.TabPages(index).Enabled Then
                            m.Result = New IntPtr(1)
                            Return
                        Else
                            Exit For
                        End If
                    End If
                Next
                m.Result = New IntPtr(-1)
                Return
        End Select

        MyBase.WndProc(m)
    End Sub


    '------------------------------------------
    '以下蛇足:選択できないタブは表示も変わってたほうが良いかなぁと強引に
    Public Sub New()
        MyBase.New()
        Me.DrawMode = TabDrawMode.OwnerDrawFixed
    End Sub

    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)

        'こちらのサイトの Tips 『TabControlのタブを自分で描画する』のコードを
        'ほぼそのまま流用させて頂いてます↓

        'StringFormatを作成
        Dim sf As New StringFormat()
        '中央に表示する
        sf.Alignment = StringAlignment.Center
        sf.LineAlignment = StringAlignment.Center

        '背景の描画
        e.Graphics.FillRectangle(New SolidBrush(SystemColors.Control), e.Bounds)
        'Textの描画
        Dim tp As TabPage = Me.TabPages(e.Index)
        Dim rectf As New RectangleF( _
            e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)
        Dim brush As Brush = New SolidBrush( _
            IIf(tp.Enabled, SystemColors.WindowText, SystemColors.GrayText))
        e.Graphics.DrawString(tp.Text, e.Font, brush, rectf, sf)

        MyBase.OnDrawItem(e)
    End Sub
End Class
2005/01/14(Fri) 13:49:13 編集(投稿者)
2005/01/14(Fri) 13:47:14 編集(投稿者)

■No8417に返信(深山さんの記事)

平ちゃんです
いつもお世話様です。

>  私もじゃんぬねっとさんが No8390 で仰った
>>そもそも、TabPage を無効にすればページ内すべてが無効になりますから、
>>わざわざ移動までさせなくする意味がわかりません。
> と同意見です。移動させたくないなら非表示にしてしまったほうが良いのでは?とも思います。
> ‥‥が、それはそれとして。

私も同意見ですが頑固なユーザーだったら・・

>
>  改善の余地は多分にありますが参考まで。


キー(←や→キー)で Enabled = False なPageを飛ばして選択が循環できたらなお 使いやすいかと。

このユーザーコントロールは私の教材にはもってこいとなりました。
有難く使わせていただきます。

Snowさん、ぢゅすさん、じゃんぬねっとさん、rikiさん、深山さん

有難うございます。
みなさん、ありがとうございます。
質問してから、ちょっといろいろあってみることができませんでした。

また、不明な点等がありましたら質問しますのでよろしくお願い致します。
解決済み!
今さらで申し訳ないのですが。。。
#8417の深山 ファミリーさんがご提案くださっている
クラスを使ってみたいのですが、

どうやって実装するのでしょうか。
(フォームに貼り付けているタブコントロールと
このクラスをどのように関連させるのでしょうか?)

基本的なことで申し訳ありません。
どなたかご教授願います。
じゃんぬねっと です。

■No8652に返信(kitaさんの記事)
> (フォームに貼り付けているタブコントロールと
> このクラスをどのように関連させるのでしょうか?)

ちゃんと読んでから投稿しましょう。

> -------------------------------------------------------------------------
■No8417に返信(深山さんの記事)
> TabControl を継承したクラスを作成するという方向から考えてみました。
> -------------------------------------------------------------------------
> ■No8652に返信(kitaさんの記事)
>>(フォームに貼り付けているタブコントロールと
>>このクラスをどのように関連させるのでしょうか?)
>
> ちゃんと読んでから投稿しましょう。

読んだつもりなのですが、
完全には理解ができていません。
質問の内容、おかしいですか?
知識不足で申し訳ありません。
じゃんぬねっと です。

■No8655に返信(kitaさんの記事)
> 読んだつもりなのですが、
> 完全には理解ができていません。
> 質問の内容、おかしいですか?
> 知識不足で申し訳ありません。
きつい言い方をしてしまいましたね... 申し訳ありません m(_ _)m

kita さんが欲しがっている機能を実装したカスタムコントロールを作成し、
そのカスタムコントロール自体をフォームに配置すれば良いのです。

ですから、

> (フォームに貼り付けているタブコントロールと
> このクラスをどのように関連させるのでしょうか?)

配置されている自身が "ソレ" ですので、関連も何も必要ないのです。
じゃんぬねっと 様

> きつい言い方をしてしまいましたね... 申し訳ありません m(_ _)m

いえいえ、気にしておりませんので。。。
こちらこそ、ご丁寧に対応していただき、
ありがとうございます。

>欲しがっている機能を実装したカスタムコントロールを作成し、
> そのカスタムコントロール自体をフォームに配置すれば良いのです。

「カスタムコントロール」初めて聞く用語です。
調べてみます。

ありがとうございました。<(_ _)>

DOBON.NET | プログラミング道 | プログラミング掲示板