トップ回答者
FlowLayoutPanelで水平スクロールバーのみを表示した状態でスクロールができません。

質問
-
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
回答
-
とりあえず変な位置にならないようにスクロール後に直してみるとか
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!)
- 回答の候補に設定 栗下 望Microsoft employee, Moderator 2016年12月20日 0:10
- 回答の候補の設定解除 栗下 望Microsoft employee, Moderator 2016年12月26日 1:20
- 回答としてマーク xxTKCxx 2017年1月12日 1:19
-
スクロールの終わりに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!)
- 回答の候補に設定 栗下 望Microsoft employee, Moderator 2016年12月21日 0:03
- 回答としてマーク 栗下 望Microsoft employee, Moderator 2016年12月26日 1:19
すべての返信
-
とりあえず変な位置にならないようにスクロール後に直してみるとか
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!)
- 回答の候補に設定 栗下 望Microsoft employee, Moderator 2016年12月20日 0:10
- 回答の候補の設定解除 栗下 望Microsoft employee, Moderator 2016年12月26日 1:20
- 回答としてマーク xxTKCxx 2017年1月12日 1:19
-
>>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
-
スクロールの終わりに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!)
- 回答の候補に設定 栗下 望Microsoft employee, Moderator 2016年12月21日 0:03
- 回答としてマーク 栗下 望Microsoft employee, Moderator 2016年12月26日 1:19
-
回答ありがとうございます。
スクロール中は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様が行ったような部分的な引用はどのように行うのでしょうか?
宜しくお願い致します。