WPF の ListBox でスクロールバーの下に隙間ができる
◆ アイテム単位のスクロールにしてると高さが合わないと下に隙間ができる
◆ ピクセル単位スクロールにする
◆ ScrollViewer.CanContentScroll="False"
◆ ピクセル単位スクロールにする
◆ ScrollViewer.CanContentScroll="False"
こんな ListBox を作ります
MaxHeight を指定して それ以上の高さになるくらいのデータを items にいれます
Scroll.Viewer.VerticalScrollBarVisibility を Auto にしてスクロールバーを表示するようにします
なんか 下空いてますよね
空文字列があるわけではないです
選択もできません
謎の余白です
横スクロールバーの分が取られているというわけでもないようです
ググっていると StackOverflow で見つけたページで
を設定すれば直ると聞いてやってみました
直ってます
false はピクセル単位で true がアイテム単位らしいです
デフォルトは false らしいのですが なぜか true になってるみたいで false に戻すと下の余白がなくなります
この 2 つのスクロール違いですが名前のとおりです
アイテム単位にしてると スクロールするときに中途半端に見える状態にならずにアイテムが完全に見える状態になります
エクセルで縦長行があるシートを縦にスクロールするときにちょうどいいところで止まらずに行が全部見れるところまで大きくスクロールしてしまう ああいうのですね
それだとアイテムが全部見えるようにする都合で余白が出るのもわかります
今回のでは ピクセル単位スクロールの余白がない方では 「t」 が見えています
ですが たぶん完全に表示できていなくてちょっとはみ出ていて それがアイテム単位スクロールでは全部表示されていないといけないので 「t」 は入らないようにして そのせいで下に余白がでているというわけです
わかりやすくサンプル
横に CanContentScroll が true のものと false のものを並べます
さっきと別のパソコンでスクショしたらフォントサイズが違って 「s」 まで表示されていますがそこは気にしないでください
右側の 「s」 は枠線の上側が表示されてないですよね
アイテム単位ではこういう中途半端はダメなので 「s」 は入れないで一番上を 「t」 にします
すると自然と下に余白ができます
一番上は全部入っていないといけないですが 下は全部入っていなくても大丈夫です
スクロールの途中では一番下が途切れてます
WPF では UI を仮想化して軽く高速に動作できる機能があり それはアイテム単位のスクロールには使えるのですが ピクセル単位のスクロールでは使えないようです
仮想化するのが必須なら高さを調節するか最後の余白は諦めることになります
仮想化すると スクロールバーで見えない部分は実体を作らず必要になったら作るらしいですが そのせいで実体がない部分を取得したときにデータが取れないとか チェックボックスをそれぞれのアイテムにつけてチェック状態を取得するときに仮想化されて実体がないところは正しく取れないとか気づきにくいバグの元があると聞いたことがあります
そういうのを聞いて何千万件のデータとか全部の表示は重いってときでもないと仮想化しないほうがいいんじゃないかなー と思ってたりします
MaxHeight を指定して それ以上の高さになるくらいのデータを items にいれます
Scroll.Viewer.VerticalScrollBarVisibility を Auto にしてスクロールバーを表示するようにします
<ListBox BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}" MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" />
なんか 下空いてますよね
空文字列があるわけではないです
選択もできません
謎の余白です
横スクロールバーの分が取られているというわけでもないようです
ググっていると StackOverflow で見つけたページで
ScrollViewer.CanContentScroll="False"
を設定すれば直ると聞いてやってみました
<ListBox BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}" MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="False" />
直ってます
CanContentScroll
これなんなんだろうと調べてみるとスクロールをピクセル単位(物理スクロール)でするかアイテム単位(論理スクロール)でするかを設定するもののようですfalse はピクセル単位で true がアイテム単位らしいです
デフォルトは false らしいのですが なぜか true になってるみたいで false に戻すと下の余白がなくなります
この 2 つのスクロール違いですが名前のとおりです
アイテム単位にしてると スクロールするときに中途半端に見える状態にならずにアイテムが完全に見える状態になります
エクセルで縦長行があるシートを縦にスクロールするときにちょうどいいところで止まらずに行が全部見れるところまで大きくスクロールしてしまう ああいうのですね
それだとアイテムが全部見えるようにする都合で余白が出るのもわかります
今回のでは ピクセル単位スクロールの余白がない方では 「t」 が見えています
ですが たぶん完全に表示できていなくてちょっとはみ出ていて それがアイテム単位スクロールでは全部表示されていないといけないので 「t」 は入らないようにして そのせいで下に余白がでているというわけです
わかりやすくサンプル
横に CanContentScroll が true のものと false のものを並べます
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0">CanContentScroll = True</Label>
<ListBox Grid.Column="0" Grid.Row="1" BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}"
MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True"/>
<Label Grid.Column="1" Grid.Row="0">CanContentScroll = False</Label>
<ListBox Grid.Column="1" Grid.Row="1" BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}"
MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="False"/>
</Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0">CanContentScroll = True</Label>
<ListBox Grid.Column="0" Grid.Row="1" BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}"
MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True"/>
<Label Grid.Column="1" Grid.Row="0">CanContentScroll = False</Label>
<ListBox Grid.Column="1" Grid.Row="1" BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}"
MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="False"/>
</Grid>
public MainWindow()
{
this.DataContext = new
{
items = "ABCDEFGHIJKLMNOPQRTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(),
};
}
{
this.DataContext = new
{
items = "ABCDEFGHIJKLMNOPQRTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(),
};
}
さっきと別のパソコンでスクショしたらフォントサイズが違って 「s」 まで表示されていますがそこは気にしないでください
右側の 「s」 は枠線の上側が表示されてないですよね
アイテム単位ではこういう中途半端はダメなので 「s」 は入れないで一番上を 「t」 にします
すると自然と下に余白ができます
一番上は全部入っていないといけないですが 下は全部入っていなくても大丈夫です
スクロールの途中では一番下が途切れてます
仮想化
スクロールの種類は仮想化にも関係してるようですWPF では UI を仮想化して軽く高速に動作できる機能があり それはアイテム単位のスクロールには使えるのですが ピクセル単位のスクロールでは使えないようです
仮想化するのが必須なら高さを調節するか最後の余白は諦めることになります
仮想化すると スクロールバーで見えない部分は実体を作らず必要になったら作るらしいですが そのせいで実体がない部分を取得したときにデータが取れないとか チェックボックスをそれぞれのアイテムにつけてチェック状態を取得するときに仮想化されて実体がないところは正しく取れないとか気づきにくいバグの元があると聞いたことがあります
そういうのを聞いて何千万件のデータとか全部の表示は重いってときでもないと仮想化しないほうがいいんじゃないかなー と思ってたりします