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

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

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

Re[4]: コントロール単位でスクロールさせたい


(過去ログ 148 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■86661 / inTopicNo.1)  コントロール単位でスクロールさせたい
  
□投稿者/ 大阪 (6回)-(2018/02/27(Tue) 00:25:26)

分類:[.NET 全般] 

環境:VB2010,Win7,WinFormアプリ

FlowLayoutPanel(AutoScroll = False)内の垂直方向に配置されたコントロールを
VScrollBarでコントロール単位でスクロールさせたいです。
コンテナ内のコントロールのサイズはすべて同じという前提です。

具体的には以下のコードのValueChangedイベントで_VScrollBar.Valueと同じインデックス
のボタンをTop位置にスクロールさせたいです。
ValueChangedイベントでどのような実装が必要か教えてください。

Public Class Form1
    Private _max As Integer = 100
    WithEvents _VScrollBar As New VScrollBar
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        With _VScrollBar
            .Maximum = _max
            .Minimum = 0
            .LargeChange = 10
            .SmallChange = 1
            .Value = 0
            .Dock = DockStyle.Right
        End With
        Dim FlowLayoutPanel1 As New FlowLayoutPanel
        With FlowLayoutPanel1
            .AutoScroll = False
            .Dock = DockStyle.Fill
            .WrapContents = False
            .FlowDirection = FlowDirection.TopDown
        End With
        For i As Integer = 0 To _max
            Dim btn As New Button
            btn.Text = i
            btn.Height = 50
            FlowLayoutPanel1.Controls.Add(btn)
        Next
        With Me
            .Controls.Add(FlowLayoutPanel1)
            .Controls.Add(_VScrollBar)
            .Text = 0
        End With
    End Sub

    Private Sub _VScrollBar_ValueChanged(sender As Object, e As System.EventArgs) Handles _VScrollBar.ValueChanged
        Me.Text = _VScrollBar.Value
        'ここで_VScrollBar.Valueと同じインデックスのボタンをTopに持ってきたいです。
    End Sub
End Class

引用返信 編集キー/
■86667 / inTopicNo.2)  Re[1]: コントロール単位でスクロールさせたい
□投稿者/ 魔界の仮面弁士 (1578回)-(2018/02/27(Tue) 10:09:03)
No86661 (大阪 さん) に返信
> FlowLayoutPanel(AutoScroll = False)内の垂直方向に配置されたコントロールを
AutoScroll = True では駄目なのですか?
自動的にスクロールバーが出るので、追加のコーディングが不要になりそうですが。

それと、キーボード(ScrollUp/ScrollDown/上矢印/下矢印/Tab/Shift+Tab)による操作や、
フォームのリサイズ(高さ変更)への追従、スクロール領域外のコントロールに
フォーカスがあることを許容するかどうかなども検討しておく必要がありそうです。


> 具体的には以下のコードのValueChangedイベントで_VScrollBar.Valueと同じインデックス
> のボタンをTop位置にスクロールさせたいです。
その仕様だと、最後のボタンを表示しきれない気もしますが、
それはとりあえず後で考えるとして。


Option Strict On
Public Class Form1
  Private _max As Integer = 100
  Private FlowLayoutPanel1 As New FlowLayoutPanel() With {.AutoScroll = False, .Dock = DockStyle.Fill, .WrapContents = False, .FlowDirection = FlowDirection.TopDown}
  Private WithEvents _VScrollBar As New VScrollBar() With {.Maximum = _max, .Minimum = 0, .LargeChange = 10, .SmallChange = 1, .Value = 0, .Dock = DockStyle.Right}
  Private Buttons As New List(Of Button)()
  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    FlowLayoutPanel1.SuspendLayout()
    For i = 0 To _max
      Dim btn As New Button() With {.Text = i.ToString()}
      btn.Height = 50
      Buttons.Add(btn)
      FlowLayoutPanel1.Controls.Add(btn)
    Next
    Controls.Add(FlowLayoutPanel1)
    FlowLayoutPanel1.ResumeLayout(False)
    Controls.Add(_VScrollBar)
    Text = "0"
  End Sub

  Private Sub _VScrollBar_ValueChanged(sender As Object, e As System.EventArgs) Handles _VScrollBar.ValueChanged
    FlowLayoutPanel1.SuspendLayout()
    For i = _max To 0 Step -1
      Buttons(i).Visible = i >= _VScrollBar.Value
    Next
    FlowLayoutPanel1.ResumeLayout(True)
    Me.Text = _VScrollBar.Value.ToString()
  End Sub
End Class


上記は、Visible プロパティを切り替えることで、スクロールされたボタンを
非表示にする方法で実装しています。

フォーカスが失われることを嫌うなら、マイナス座標に別のコンテナを用意しておき、
ボタンをそこに移動させるという手もありそうです。
引用返信 編集キー/
■86672 / inTopicNo.3)  Re[2]: コントロール単位でスクロールさせたい
□投稿者/ 大阪 (7回)-(2018/02/27(Tue) 12:14:31)
No86667 (魔界の仮面弁士 さん) に返信
ありがとうございます。
提示頂いたコードで期待通りの動作をしました。

> AutoScroll = True では駄目なのですか?
特に駄目と云う事ではないですけど、コードから動的にコントロールを追加、削除を
繰り返すような実装になっている場合、自動でスクロールバーが出たり消えたりする為、
スクロールバーの有無でコントロールの幅が変わるので(縦スクロールバーの場合)、
それが嫌なケースもあります。
特に今回、何かアプリを作成しているわけではないので、具体的にといわれると困りますが... 


> それと、キーボード(ScrollUp/ScrollDown/上矢印/下矢印/Tab/Shift+Tab)による操作や、
マウスホイールと上下キーには以下のコードで対応しました。

> フォームのリサイズ(高さ変更)への追従、
フォームのリサイズ(高さ変更)への追従はちょっと意味がわかりませんでした。
リサイズ時に問題がでそうな事項があれば、教えてください。

> スクロール領域外のコントロールにフォーカスがあることを許容するかどうかなども検討しておく必要がありそうです。
これはケースバイケースで対応します。


> その仕様だと、最後のボタンを表示しきれない気もしますが、
> それはとりあえず後で考えるとして。
これは、質問した後、気付きました。
これについては、VScrollBarのMaximumプロパティを変更する事で、対応しました。

もし何か、お気づきの点があれば、ご指摘ください。

Option Strict On
Public Class Form1
    Private _max As Integer = 100
    Private WithEvents FlowLayoutPanel1 As New FlowLayoutPanel() With {.AutoScroll = False, .Dock = DockStyle.Fill, .WrapContents = False, .FlowDirection = FlowDirection.TopDown}
    Private WithEvents _VScrollBar As New VScrollBar() With {.Maximum = _max, .Minimum = 0, .LargeChange = 10, .SmallChange = 1, .Value = 0, .Dock = DockStyle.Right}
    Private Buttons As New List(Of Button)()

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        FlowLayoutPanel1.SuspendLayout()
        For i = 0 To _max
            Dim btn As New Button() With {.Text = i.ToString()}
            btn.Height = 50
            AddHandler btn.PreviewKeyDown, AddressOf btn_PreviewKeyDown
            AddHandler btn.MouseWheel, AddressOf btn_MouseWheel
            Buttons.Add(btn)
            FlowLayoutPanel1.Controls.Add(btn)
        Next
        Controls.Add(FlowLayoutPanel1)
        FlowLayoutPanel1.ResumeLayout(False)
        Controls.Add(_VScrollBar)
        Text = "0"
        '最後のボタンを表示しきれない対応
        With _VScrollBar
            .Maximum = .Maximum + .LargeChange - 1
        End With

    End Sub
    Private Sub _VScrollBar_ValueChanged(sender As Object, e As System.EventArgs) Handles _VScrollBar.ValueChanged
        FlowLayoutPanel1.SuspendLayout()
        For i = _max To 0 Step -1
            Buttons(i).Visible = i >= _VScrollBar.Value
        Next
        FlowLayoutPanel1.ResumeLayout(True)
        Me.Text = _VScrollBar.Value.ToString()
    End Sub

    Private Sub btn_PreviewKeyDown(sender As Object, e As System.Windows.Forms.PreviewKeyDownEventArgs)
        Select Case e.KeyCode
            Case Keys.Up
                _VScrollBar.Value = If(_VScrollBar.Value - 1 < 0, 0, _VScrollBar.Value - 1)
            Case Keys.Down
                _VScrollBar.Value = If(_VScrollBar.Value + 1 > _max, _max, _VScrollBar.Value + 1)
        End Select
    End Sub

    Private Sub btn_MouseWheel(sender As Object, e As System.Windows.Forms.MouseEventArgs)
        Select Case Math.Sign(e.Delta)
            Case 1
                _VScrollBar.Value = If(_VScrollBar.Value - 1 < 0, 0, _VScrollBar.Value - 1)
            Case -1
                _VScrollBar.Value = If(_VScrollBar.Value + 1 > _max, _max, _VScrollBar.Value + 1)
        End Select
    End Sub
End Class


引用返信 編集キー/
■86674 / inTopicNo.4)  Re[3]: コントロール単位でスクロールさせたい
□投稿者/ 魔界の仮面弁士 (1579回)-(2018/02/27(Tue) 12:40:34)
No86672 (大阪 さん) に返信
>> その仕様だと、最後のボタンを表示しきれない気もしますが、
> これは、質問した後、気付きました。
> これについては、VScrollBarのMaximumプロパティを変更する事で、対応しました。

VScrollBar のつまみを最下段まで下げたとしても、
Value プロパティは最大値になりませんので、
スクロールバーの可動粋は、それを考慮する必要がありますからね。


> リサイズ時に問題がでそうな事項があれば、教えてください。

フォームサイズが変われば、FlowLayoutPanel 内に
表示可能なボタンの数も変わりますので、それを
「VScrollBarのMaximumプロパティを変更する事」に
追従させるか否かということです。


また、最大値までスクロールさせた場合に、最後のボタンが
最下段にあった方が良いのか、それとも
最上段にあった方が良いのか(その下が空欄になる)でも
スクロールバーに求める可動域が変化してくるでしょうね。
引用返信 編集キー/
■86675 / inTopicNo.5)  Re[4]: コントロール単位でスクロールさせたい
□投稿者/ 大阪 (8回)-(2018/02/27(Tue) 13:17:41)
No86674 (魔界の仮面弁士 さん) に返信
> フォームサイズが変われば、FlowLayoutPanel 内に
> 表示可能なボタンの数も変わりますので、それを
> 「VScrollBarのMaximumプロパティを変更する事」に
> 追従させるか否かということです。
解りました。
ちなみに、コンテナに現在、表示されているコントロールの数を得る
プロパティがあったりしますか?
計算で得るにはコンテナやコントロールのパディングやマージンを考慮
する必要がありますよね。

> また、最大値までスクロールさせた場合に、最後のボタンが
> 最下段にあった方が良いのか、それとも
> 最上段にあった方が良いのか(その下が空欄になる)でも
> スクロールバーに求める可動域が変化してくるでしょうね。
AutoScroll = Trueだと、前者、現在のコードだと後者ということですね。
これもケースバイケースで対応するようにします。
引用返信 編集キー/
■86677 / inTopicNo.6)  Re[5]: コントロール単位でスクロールさせたい
□投稿者/ 大阪 (9回)-(2018/02/27(Tue) 14:12:47)
No86675 (大阪 さん) に返信
> ちなみに、コンテナに現在、表示されているコントロールの数を得る
> プロパティがあったりしますか?
> 計算で得るにはコンテナやコントロールのパディングやマージンを考慮
> する必要がありますよね。

間違ってるかもしれないけど、以下で一応それらしい値は得られました。

'LoadイベントでAddHandler btn.Click, AddressOf btn_Clickを追加
  Private Sub btn_Click(sender As System.Object, e As System.EventArgs)
        Dim realH As Integer = FlowLayoutPanel1.Height - FlowLayoutPanel1.Padding.Top - FlowLayoutPanel1.Padding.Bottom
        Dim btn As Button = DirectCast(sender, Button)
        Dim m As Integer = btn.Margin.Top + btn.Margin.Bottom
        MsgBox(FlowLayoutPanel1.Height / (m + btn.Height))
    End Sub

引用返信 編集キー/
■86679 / inTopicNo.7)  Re[6]: コントロール単位でスクロールさせたい
□投稿者/ 大阪 (10回)-(2018/02/27(Tue) 14:25:04)
No86677 (大阪 さん) に返信
> ■No86675 (大阪 さん) に返信
訂正

  Private Sub btn_Click(sender As System.Object, e As System.EventArgs)
        Dim realH As Integer = FlowLayoutPanel1.Height - FlowLayoutPanel1.Padding.Top - FlowLayoutPanel1.Padding.Bottom
        Dim btn As Button = DirectCast(sender, Button)
        Dim m As Integer = btn.Margin.Top + btn.Margin.Bottom

        MsgBox(Math.Min(_max - _VScrollBar.Value + 1, realH / (m + btn.Height)))
    End Sub

引用返信 編集キー/
■86692 / inTopicNo.8)  Re[4]: コントロール単位でスクロールさせたい
□投稿者/ 大阪 (11回)-(2018/02/28(Wed) 09:28:38)
魔界の仮面弁士 さん

どうも、ありがとうございました。
これで解決とさせて頂きます。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -