しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

GridView の 表示領域をすき間なく詰めるには

GridView を下記のようにシンプルに記述した場合

<GridView Grid.RowSpan="2"
            ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
            Padding="116,186,116,0">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsWrapGrid ItemHeight="250"
                           ItemWidth="300" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Border Background="Red" Padding="20">
                <TextBlock FontSize="32"
                            Foreground="White"
                            Style="{StaticResource BaseTextBlockStyle}"
                     Text="コンテンツ領域"/>
            </Border>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

横幅 300 px、縦幅 250 px の赤いタイルが表示されると思われますが・・・

f:id:matatabi_ux:20140720145640p:plain

実際はこんな風に赤い部分は中央に寄せられてしまい、周囲にすき間が開いてしまいます

このすき間を詰めるにはどうしたらよいか

とりあえず各領域がわかるように色を付けてみます

<GridView Grid.RowSpan="2"
            ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
            Padding="116,186,116,0">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsWrapGrid Background="Khaki"
                            ItemHeight="250"
                            ItemWidth="300" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="Background" Value="DarkOrange" />
        </Style>
    </GridView.ItemContainerStyle>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Border Background="Red" Padding="20">
                <TextBlock FontSize="32"
                            Foreground="White"
                            Style="{StaticResource BaseTextBlockStyle}"
                            Text="コンテンツ領域" />
            </Border>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

するとこんな感じに

f:id:matatabi_ux:20140720150322p:plain

DarkOrange の部分が ItemContainer の領域で、Khaki の部分が ItemsPanel の領域になります

コンテンツ領域と ItemContainer の領域にかなりすき間があるのでまずここを詰めてみます

<GridView Grid.RowSpan="2"
            ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
            Padding="116,186,116,0">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsWrapGrid Background="Khaki"
                            ItemHeight="250"
                            ItemWidth="300" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="Background" Value="DarkOrange" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="VerticalContentAlignment" Value="Stretch" />
        </Style>
    </GridView.ItemContainerStyle>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Border Background="Red" Padding="20">
                <TextBlock FontSize="32"
                            Foreground="White"
                            Style="{StaticResource BaseTextBlockStyle}"
                            Text="コンテンツ領域" />
            </Border>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

ItemContainerStyle の Margin を 0 にして、HorizontalContentAlignment と VerticalContentAlignment を Stretch にしました

f:id:matatabi_ux:20140720151249p:plain

すると DarkOrange の領域が消え、赤いコンテンツ領域が広がりました

ただ、まだ Khaki の部分にはすき間が残っています・・・これはどういうことかということで、さらに GridViewItem の ControlTemplate を調べてみると下記のように記述されていました

  <Style TargetType="GridViewItem" x:Key="GridViewItemExpanded">
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="TabNavigation" Value="Local" />
    <Setter Property="IsHoldingEnabled" Value="True" />
    <Setter Property="Margin" Value="0,0,2,2" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="GridViewItem">
          <Border x:Name="OuterContainer">
            <VisualStateManager.VisualStateGroups>

              ~ 中略 ~

            </VisualStateManager.VisualStateGroups>
            <Grid x:Name="ReorderHintContent" Background="Transparent">
              <Path x:Name="SelectingGlyph" Opacity="0" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckSelectingThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,9.5,9.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight" />
              <Border x:Name="HintGlyphBorder" Height="40" Width="40" HorizontalAlignment="Right" VerticalAlignment="Top" Opacity="0" Margin="4">
                <Path x:Name="HintGlyph" Opacity="0" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckHintThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,5.5,5.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight" />
              </Border>
              <Border x:Name="ContentContainer">
                <!-- This extra wrapper grid is necessary because rendertransforms set by the reorder hint animations
                                     will be lost when ContentContainer becomes a LTE -->
                <Grid x:Name="InnerDragContent">
                  <Rectangle x:Name="PointerOverBorder" IsHitTestVisible="False" Opacity="0" Fill="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}" Margin="1" />
                  <Rectangle x:Name="FocusVisual" IsHitTestVisible="False" Opacity="0" StrokeThickness="2" Stroke="{ThemeResource ListViewItemFocusBorderThemeBrush}" />
                  <Rectangle x:Name="SelectionBackground" Margin="4" Fill="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" Opacity="0" />
                  <Border x:Name="ContentBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="4">
                    <Grid>
                      <ContentPresenter x:Name="contentPresenter" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" />
                      <!-- The 'Xg' text simulates the amount of space one line of text will occupy.
                                                 In the DataPlaceholder state, the Content is not loaded yet so we
                                                 approximate the size of the item using placeholder text. -->
                      <TextBlock x:Name="PlaceholderTextBlock" Visibility="Collapsed" Text="Xg" Foreground="{x:Null}" Margin="{TemplateBinding Padding}" IsHitTestVisible="False" AutomationProperties.AccessibilityView="Raw" />
                      <Rectangle x:Name="PlaceholderRect" Visibility="Collapsed" Fill="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" />
                      <Rectangle x:Name="MultiArrangeOverlayBackground" IsHitTestVisible="False" Opacity="0" Fill="{ThemeResource ListViewItemDragBackgroundThemeBrush}" />
                    </Grid>
                  </Border>
                  <Rectangle x:Name="SelectedBorder" IsHitTestVisible="False" Opacity="0" Stroke="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" StrokeThickness="{ThemeResource GridViewItemSelectedBorderThemeThickness}" Margin="4" />
                  <Border x:Name="SelectedCheckMarkOuter" IsHitTestVisible="False" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="4">
                    <Grid x:Name="SelectedCheckMark" Opacity="0" Height="40" Width="40">
                      <Path x:Name="SelectedEarmark" Data="M0,0 L40,0 L40,40 z" Fill="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" Stretch="Fill" />
                      <Path Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{ThemeResource ListViewItemCheckThemeBrush}" Height="13" Stretch="Fill" Width="15" HorizontalAlignment="Right" Margin="0,5.5,5.5,0" VerticalAlignment="Top" FlowDirection="LeftToRight" />
                    </Grid>
                  </Border>
                  <TextBlock x:Name="MultiArrangeOverlayText" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.DragItemsCount}" Foreground="{ThemeResource ListViewItemDragForegroundThemeBrush}" FontFamily="{ThemeResource ContentControlThemeFontFamily}" FontSize="26.667" IsHitTestVisible="False" Opacity="0" TextWrapping="Wrap" TextTrimming="WordEllipsis" Margin="18,9,0,0" AutomationProperties.AccessibilityView="Raw" />
                </Grid>
              </Border>
            </Grid>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

これを見ると ContentBorder の部分

 <Border x:Name="ContentBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="4">
                    <Grid>
                      <ContentPresenter x:Name="contentPresenter" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" />

Margin に 4 が指定されているため、これが原因ですき間があいたようです

これは選択枠などのインタラクションを重ねて表示するために確保されているようですが、コンテンツ領域をぴったりすき間なく詰めたいという場合は余計な余白になります

ControlTemplate を書き換えて Margin を 0 にしてもよいですが、少し面倒くさいです

<GridView Grid.RowSpan="2"
    ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
    Padding="116,186,116,0">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsWrapGrid Background="Khaki"
                    ItemHeight="250"
                    ItemWidth="300" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="Background" Value="DarkOrange" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="Padding" Value="-4" />
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="VerticalContentAlignment" Value="Stretch" />
        </Style>
    </GridView.ItemContainerStyle>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Border Background="Red" Padding="20">
                <TextBlock FontSize="32"
                    Foreground="White"
                    Style="{StaticResource BaseTextBlockStyle}"
                    Text="コンテンツ領域" />
            </Border>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

幸いコンテンツ領域を内包して表示する ContentPresenter が Margin に テンプレートプロパティの Padding がバインドされているので、これを利用して上記のように記載してみます

f:id:matatabi_ux:20140720153117p:plain

今度こそぴったりとすき間なく詰まりました