none
FlowLayoutPanelで水平スクロールバーのみを表示した状態でスクロールができません。 RRS feed

  • 質問

  • VisualStudioCommunity 2015,VB.net 4.6を使用しています。

    OSはWin7 64bit Professionalです。

    フォームにFlowLayoutPanelを設置し、AutoScrollではなくHorizontalScrollのVisibleプロパティをONに設定し、

    フォームのロードイベントで、Maximumを500、Minimumを0とし、Value値に100を設定しています。

    フォームが起動した状態では、スクロールの位置は100の位置になっていますが、ここでマウスを使ってスクロールさせると、スクロール終了後一番左に戻ってしまいます。(Value値が0になる)

    スクロールを移動させた際に発生するスクロールイベントで、Value値にスクロール値を設定しているのですが、スクロール中はValueに値が入っていても、スクロールが終了すると戻ってしまうようです。

    ちなみに、Panelコントロールでも試してみましたが、同じ現象でした。

    ボタンを配置し、ボタンを押すたびにValue値をアップしていくというのは問題なく行えます。

    スクロールした後、その状態をそのままスクロールバーに反映させるためには、どのように設定すればすればよいのでしょうか。

    ご回答よろしくお願いいたします。

    Public Class Form2
        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            With FlowLayoutPanel1
                With .HorizontalScroll
                    .Visible = True
                    .Enabled = True
                    .Minimum = 0
                    .Maximum = 500
                    .Value = 100
                End With
            End With
    
            With Panel1
                With .HorizontalScroll
                    .Visible = True
                    .Enabled = True
                    .Minimum = 0
                    .Maximum = 500
                    .Value = 100
                End With
            End With
        End Sub
    
        Private Sub FlowLayoutPanel1_Scroll(sender As Object, e As ScrollEventArgs) Handles FlowLayoutPanel1.Scroll
            With DirectCast(sender, FlowLayoutPanel)
                With .HorizontalScroll
                    If .Value <> e.NewValue Then
                        .Value = e.NewValue
                        Console.WriteLine("NewValue:{0},Value:{1}", e.NewValue, .Value)
                    End If
                End With
            End With
        End Sub
    
        Private Sub Panel1_Scroll(sender As Object, e As ScrollEventArgs) Handles Panel1.Scroll
            With DirectCast(sender, Panel)
                With .HorizontalScroll
                    If .Value <> e.NewValue Then
                        .Value = e.NewValue
                        Console.WriteLine("NewValue:{0},Value:{1}", e.NewValue, .Value)
                    End If
                End With
            End With
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            With FlowLayoutPanel1.HorizontalScroll
                Debug.Print(.Value.ToString)
                .Value = .Value + 10
            End With
        End Sub
    End Class



    • 編集済み xxTKCxx 2016年12月19日 5:10
    2016年12月19日 4:49

回答

  • とりあえず変な位置にならないようにスクロール後に直してみるとか

    Private Sub FlowLayoutPanel1_Scroll(sender As Object, e As ScrollEventArgs) Handles FlowLayoutPanel1.Scroll
    
        Dim sc As ScrollableControl = CType(sender, ScrollableControl) 'ScrollableControlはFlowlayoutPanelとPanelの共通の親クラス
    
        If (e.ScrollOrientation = ScrollOrientation.HorizontalScroll) Then
            Select Case (e.Type)
                Case ScrollEventType.ThumbPosition
                    Dim x As Integer = e.NewValue
                    sc.HorizontalScroll.Enabled = False '無効にすることで一瞬ゼロになって見えるのを回避
    
                    '無効にするとEndScrollでイベントが呼ばれなくなる
                    'WndprocをオーバーライドしてSB_ENDSCROLLを使ったほうが確実だとおもうけど
                    '全部をオーバーライドは大変なのでBeginInvokeで逃げてみる
                    Me.BeginInvoke(Sub()
                                        sc.HorizontalScroll.Value = x 'スクロール後に改めて値を入れ直し
                                        sc.HorizontalScroll.Enabled = True
                                    End Sub)
                Case ScrollEventType.EndScroll
                    'sc.HorizontalScroll.Value = x
                    'sc.HorizontalScroll.Enabled = True
            End Select
        Else
            Select Case (e.Type)
                Case ScrollEventType.ThumbPosition
                    Dim y As Integer = e.NewValue
                    sc.VerticalScroll.Enabled = False
                    Me.BeginInvoke(Sub()
                                        sc.VerticalScroll.Value = y
                                        sc.VerticalScroll.Enabled = True
                                    End Sub)
                Case ScrollEventType.EndScroll
                    'sc.VerticalScroll.Value = y
                    'sc.VerticalScroll.Enabled = True
            End Select
        End If
    
    End Sub

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2016年12月19日 10:16
  • スクロールの終わりにScrollEventType.ThumbPositionのイベントが発生してからScwollEventType.EndScrollの間に、勝手に位置が変わってしまうので、ThumbPositionでEnabled=falseにすることでスクロールバーの描画を抑制します。
    その後(本来ならEndScroll)で値をもとに戻してEnabled=trueにすることで、一瞬不正な位置で描画されないようにしています。
    >SB_ENDSCROLLを使った方が確実だと思うけど...以下略
    というのは、あまりよくわかっていなくて恐縮ですが、Invokeメソッドで処理を非同期で投げるために、すぐに処理された場合、スクロール後ではなく、スクロール中に値が入れられてしまうかもしれないという解釈でよろしいでしょうか?

    EndScrollよりも後で位置を修正する必要があるため、BeginInvokeでの処理が本来のEndScrollのタイミングよりも前に実行されてしまうと、そのあとで勝手な位置になってしまう可能性はありえます。(試した限りでは発生してませんが)

    また、スクロール中も描画されるように下記コードにしてみした。
    ところが、今度はスクロール中に、スクロールバーがちらつきます。

    不必要にEnalbedを切り替えているため、切り替えのたびにスクロールバーが有効な状態での描画と、無効な状態の描画を繰り返すことになってしまっているのでチラツキが発生します。(無効な状態でスクロール操作するという異常な状態なので)
    上述したようにThumbPositionとEndScrollの間でのみ不正な位置になるので、ThumbTrackのときはBeginInvokeもEnabledの切り替えも必要ありません。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2016年12月20日 11:37

すべての返信

  • とりあえず変な位置にならないようにスクロール後に直してみるとか

    Private Sub FlowLayoutPanel1_Scroll(sender As Object, e As ScrollEventArgs) Handles FlowLayoutPanel1.Scroll
    
        Dim sc As ScrollableControl = CType(sender, ScrollableControl) 'ScrollableControlはFlowlayoutPanelとPanelの共通の親クラス
    
        If (e.ScrollOrientation = ScrollOrientation.HorizontalScroll) Then
            Select Case (e.Type)
                Case ScrollEventType.ThumbPosition
                    Dim x As Integer = e.NewValue
                    sc.HorizontalScroll.Enabled = False '無効にすることで一瞬ゼロになって見えるのを回避
    
                    '無効にするとEndScrollでイベントが呼ばれなくなる
                    'WndprocをオーバーライドしてSB_ENDSCROLLを使ったほうが確実だとおもうけど
                    '全部をオーバーライドは大変なのでBeginInvokeで逃げてみる
                    Me.BeginInvoke(Sub()
                                        sc.HorizontalScroll.Value = x 'スクロール後に改めて値を入れ直し
                                        sc.HorizontalScroll.Enabled = True
                                    End Sub)
                Case ScrollEventType.EndScroll
                    'sc.HorizontalScroll.Value = x
                    'sc.HorizontalScroll.Enabled = True
            End Select
        Else
            Select Case (e.Type)
                Case ScrollEventType.ThumbPosition
                    Dim y As Integer = e.NewValue
                    sc.VerticalScroll.Enabled = False
                    Me.BeginInvoke(Sub()
                                        sc.VerticalScroll.Value = y
                                        sc.VerticalScroll.Enabled = True
                                    End Sub)
                Case ScrollEventType.EndScroll
                    'sc.VerticalScroll.Value = y
                    'sc.VerticalScroll.Enabled = True
            End Select
        End If
    
    End Sub

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2016年12月19日 10:16
  • >>gekka様

    回答いただきありがとうございました。

    確かにいただいたコードで指定した位置で止まるようになりました。
    ありがとうございます。

    >SB_ENDSCROLLを使った方が確実だと思うけど...以下略
    というのは、あまりよくわかっていなくて恐縮ですが、Invokeメソッドで処理を非同期で投げるために、すぐに処理された場合、スクロール後ではなく、スクロール中に値が入れられてしまうかもしれないという解釈でよろしいでしょうか?

    また、スクロール中も描画されるように下記コードにしてみした。
    ところが、今度はスクロール中に、スクロールバーがちらつきます。

    なぜでしょうか。
    どのようにしたらちらつきを抑えられますか?

    宜しくお願い致します。

        Private Sub FlowLayoutPanel1_Scroll(sender As Object, e As ScrollEventArgs) Handles FlowLayoutPanel1.Scroll
            Dim sc As ScrollableControl = CType(sender, ScrollableControl) 'ScrollableControlはFlowlayoutPanelとPanelの共通の親クラス
    
            If (e.ScrollOrientation = ScrollOrientation.HorizontalScroll) Then
                Select Case (e.Type)
                    Case ScrollEventType.ThumbPosition
                        Dim x As Integer = e.NewValue
                        'sc.HorizontalScroll.Enabled = False '無効にすることで一瞬ゼロになって見えるのを回避
    
                        '無効にするとEndScrollでイベントが呼ばれなくなる
                        'WndprocをオーバーライドしてSB_ENDSCROLLを使ったほうが確実だとおもうけど
                        '全部をオーバーライドは大変なのでBeginInvokeで逃げてみる
                        Me.BeginInvoke(Sub()
                                           sc.HorizontalScroll.Enabled = False
                                           sc.HorizontalScroll.Value = x 'スクロール後に改めて値を入れ直し
                                           sc.HorizontalScroll.Enabled = True
                                       End Sub)
                    Case ScrollEventType.ThumbTrack
                        Me.BeginInvoke(Sub()
                                           sc.HorizontalScroll.Enabled = False
                                           sc.HorizontalScroll.Value = e.NewValue
                                           sc.HorizontalScroll.Enabled = True
                                       End Sub)
                    Case ScrollEventType.EndScroll
                        'sc.HorizontalScroll.Value = x
                        'sc.HorizontalScroll.Enabled = True
                End Select
            Else
                Select Case (e.Type)
                    Case ScrollEventType.ThumbPosition
                        Dim y As Integer = e.NewValue
                        sc.VerticalScroll.Enabled = False
                        Me.BeginInvoke(Sub()
                                           sc.VerticalScroll.Value = y
                                           sc.VerticalScroll.Enabled = True
                                       End Sub)
                    Case ScrollEventType.EndScroll
                        'sc.VerticalScroll.Value = y
                        'sc.VerticalScroll.Enabled = True
                End Select
            End If
        End Sub

    2016年12月20日 9:46
  • スクロールの終わりにScrollEventType.ThumbPositionのイベントが発生してからScwollEventType.EndScrollの間に、勝手に位置が変わってしまうので、ThumbPositionでEnabled=falseにすることでスクロールバーの描画を抑制します。
    その後(本来ならEndScroll)で値をもとに戻してEnabled=trueにすることで、一瞬不正な位置で描画されないようにしています。
    >SB_ENDSCROLLを使った方が確実だと思うけど...以下略
    というのは、あまりよくわかっていなくて恐縮ですが、Invokeメソッドで処理を非同期で投げるために、すぐに処理された場合、スクロール後ではなく、スクロール中に値が入れられてしまうかもしれないという解釈でよろしいでしょうか?

    EndScrollよりも後で位置を修正する必要があるため、BeginInvokeでの処理が本来のEndScrollのタイミングよりも前に実行されてしまうと、そのあとで勝手な位置になってしまう可能性はありえます。(試した限りでは発生してませんが)

    また、スクロール中も描画されるように下記コードにしてみした。
    ところが、今度はスクロール中に、スクロールバーがちらつきます。

    不必要にEnalbedを切り替えているため、切り替えのたびにスクロールバーが有効な状態での描画と、無効な状態の描画を繰り返すことになってしまっているのでチラツキが発生します。(無効な状態でスクロール操作するという異常な状態なので)
    上述したようにThumbPositionとEndScrollの間でのみ不正な位置になるので、ThumbTrackのときはBeginInvokeもEnabledの切り替えも必要ありません。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2016年12月20日 11:37
  • 回答ありがとうございます。
    スクロール中はEnabledをいじらないようにしたところ、思い通りの動作ができました。
    各イベントのタイミングについても教えていただきよく理解できました。

    また、それらいただいた情報からすると、ThumbPoisitionのイベント発生時に、描画を一旦止め、EndScrollイベントで値を入れなおした後、再度描画させればよいと思ったので、ThumbPoisitionのイベントでInvalidateメソッドを呼び出し、EndScrollイベントでUpdateメソッドを実行するようにしたところ、こちらも思い通りの動きになりましたが、これよる不都合はなにか考えられるでしょうか?

    実際にはEndScrollでUpdateメソッドを呼び出さなくても描画されているので、内部ではEndScrollで再描画されているということでしょうか。

    Private Sub FlowLayoutPanel1_Scroll(sender As Object, e As ScrollEventArgs) Handles FlowLayoutPanel1.Scroll Dim sc As ScrollableControl = CType(sender, ScrollableControl) 'ScrollableControlはFlowlayoutPanelとPanelの共通の親クラス If (e.ScrollOrientation = ScrollOrientation.HorizontalScroll) Then Select Case (e.Type) Case ScrollEventType.ThumbPosition 'Dim x As Integer = e.NewValue 'sc.HorizontalScroll.Enabled = False '無効にすることで一瞬ゼロになって見えるのを回避 '無効にするとEndScrollでイベントが呼ばれなくなる 'WndprocをオーバーライドしてSB_ENDSCROLLを使ったほうが確実だとおもうけど '全部をオーバーライドは大変なのでBeginInvokeで逃げてみる 'sc.HorizontalScroll.Enabled = False 'Me.BeginInvoke(Sub() ' sc.HorizontalScroll.Value = x 'スクロール後に改めて値を入れ直し ' ' sc.HorizontalScroll.Enabled = True ' sc.Update() ' End Sub) sc.Invalidate() Case ScrollEventType.ThumbTrack sc.HorizontalScroll.Value = e.NewValue Case ScrollEventType.EndScroll 'sc.HorizontalScroll.Value = x 'sc.HorizontalScroll.Enabled = True sc.Update() End Select Else Select Case (e.Type) Case ScrollEventType.ThumbPosition Dim y As Integer = e.NewValue sc.VerticalScroll.Enabled = False Me.BeginInvoke(Sub() sc.VerticalScroll.Value = y sc.VerticalScroll.Enabled = True End Sub) Case ScrollEventType.EndScroll 'sc.VerticalScroll.Value = y 'sc.VerticalScroll.Enabled = True sc.HorizontalScroll.Value = e.NewValue 'スクロール後に改めて値を入れ直し End Select End If End Sub

    それから、本件とは関係ない話で申し訳ありませんが、私への返信でgekka様が行ったような部分的な引用はどのように行うのでしょうか?

    宜しくお願い致します。

    2016年12月21日 0:52