2009-07-10 [長年日記]

[WPF] ListBoxにProgressBarを入れるとレイアウトがおかしくなる

WPFでListBoxのアイテムにProgressBarを入れることを考えます。実際に使うときには、ItemTemplateを使ってバインドするのですが、簡単のために直にデータを書くとこんな感じになります。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <ListBox>
    <ListBox.Items>
      <ListBoxItem>
        <StackPanel>
          <TextBlock>Download Windows 7 Release Candidate with a TechNet Plus Subscription.</TextBlock>
          <ProgressBar Height="12" Value="50" />
        </StackPanel>
      </ListBoxItem>
    </ListBox.Items>
  </ListBox>
</Page>

これだと、TextBlockのサイズに合わせてプログレスバーの長さが変わってしまうので、HorizontalContentAlignmentを指定して幅を広げるようにします。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <ListBox>
    <ListBox.Resources>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
      </Style>
    </ListBox.Resources>
    <ListBox.Items>
      <ListBoxItem>
        <StackPanel>
          <TextBlock>Download Windows 7 Release Candidate with a TechNet Plus Subscription.</TextBlock>
          <ProgressBar Height="12" Value="50" />
        </StackPanel>
      </ListBoxItem>
    </ListBox.Items>
  </ListBox>
</Page>

さらにウィンドウのサイズを小さくすると横スクロールバーが出てしまうのに対処するために、MaxWidthをScrollViewerのビューポートの幅に設定するようにします。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <ListBox>
    <ListBox.Resources>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="MaxWidth" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}" />
      </Style>
    </ListBox.Resources>
    <ListBox.Items>
      <ListBoxItem>
        <StackPanel>
          <TextBlock>Download Windows 7 Release Candidate with a TechNet Plus Subscription.</TextBlock>
          <ProgressBar Height="12" Value="50" />
        </StackPanel>
      </ListBoxItem>
    </ListBox.Items>
  </ListBox>
</Page>

するとなぜだかウィンドウのサイズを小さくしたときに、コンテンツが右に移動してしまうようになります。Virtualizing Stack Panel doesn't resize correctlyによると、これは.NET Frameworkのバグのようです。VirtualizingStackPanelを使わなければ大丈夫なようなので、ListBoxのItemsPanelを普通のStackPanelにすると直ります。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <ListBox>
    <ListBox.Resources>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="MaxWidth" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}" />
      </Style>
    </ListBox.Resources>
    <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
        <StackPanel />
      </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.Items>
      <ListBoxItem>
        <StackPanel>
          <TextBlock TextTrimming="CharacterEllipsis">Download Windows 7 Release Candidate with a TechNet Plus Subscription.</TextBlock>
          <ProgressBar Height="12" Value="50" />
        </StackPanel>
      </ListBoxItem>
    </ListBox.Items>
  </ListBox>
</Page>

ついでに、TextBlockにTextTrimmingを指定してはみ出た部分を「...」にするようにしています。

ただし、VirtualizingStackPanelの代わりにStackPanelを使っている都合上、アイテムの数が多くなるとパフォーマンス的に問題が出るかもしれません。


トップ «前の日記(2009-07-08) 最新 次の日記(2009-07-11)»