◆ アイテム単位のスクロールにしてると高さが合わないと下に隙間ができる
◆ ピクセル単位スクロールにする
  ◆ ScrollViewer.CanContentScroll="False" 

こんな ListBox を作ります
MaxHeight を指定して それ以上の高さになるくらいのデータを items にいれます
Scroll.Viewer.VerticalScrollBarVisibility を Auto にしてスクロールバーを表示するようにします
<ListBox BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}" MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" />

wpfsmp12

なんか 下空いてますよね

空文字列があるわけではないです

選択もできません
謎の余白です


横スクロールバーの分が取られているというわけでもないようです

ググっていると StackOverflow で見つけたページで
ScrollViewer.CanContentScroll="False"

を設定すれば直ると聞いてやってみました
<ListBox BorderBrush="Pink" BorderThickness="1" ItemsSource="{Binding items}" MaxHeight="150" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="False" />

wpfsmp11

直ってます

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>
public MainWindow()
{
    this.DataContext = new
    {
        items = "ABCDEFGHIJKLMNOPQRTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(),
    };
}

wpfsmp14

さっきと別のパソコンでスクショしたらフォントサイズが違って 「s」 まで表示されていますがそこは気にしないでください

右側の 「s」 は枠線の上側が表示されてないですよね
アイテム単位ではこういう中途半端はダメなので 「s」 は入れないで一番上を 「t」 にします
すると自然と下に余白ができます

一番上は全部入っていないといけないですが 下は全部入っていなくても大丈夫です

wpfsmp13

スクロールの途中では一番下が途切れてます

仮想化

スクロールの種類は仮想化にも関係してるようです

WPF では UI を仮想化して軽く高速に動作できる機能があり それはアイテム単位のスクロールには使えるのですが ピクセル単位のスクロールでは使えないようです

仮想化するのが必須なら高さを調節するか最後の余白は諦めることになります


仮想化すると スクロールバーで見えない部分は実体を作らず必要になったら作るらしいですが そのせいで実体がない部分を取得したときにデータが取れないとか チェックボックスをそれぞれのアイテムにつけてチェック状態を取得するときに仮想化されて実体がないところは正しく取れないとか気づきにくいバグの元があると聞いたことがあります

そういうのを聞いて何千万件のデータとか全部の表示は重いってときでもないと仮想化しないほうがいいんじゃないかなー と思ってたりします