سؤال

I can easily "implicitly" style a ListView in basic (non-GridView) mode but my attempts to implicitly style a ListView in GridView mode have failed miserably. The below works because I explicity set the Style and ItemContainerStyle of the second ListView. If you remove those two settings, the second ListView does not get implicitly styled liked the first one does. It seems that a basic ListView needs a ContentPresenter and a GridView ListView needs a GridViewRowPresenter.

Am I runnning into a WPF brick wall here? Is this even possible? If not, it makes creating an application skin less robust because now your users have to know to explicitly set the Style and ItemContainerStyle on ListViews that display in GridView mode.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="ListView">
            <Setter Property="Background" Value="Lime"/>
        </Style>
        <Style TargetType="ListViewItem">
            <Setter Property="Background" Value="Yellow"/>
            <Setter Property="Template">
              <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Grid>
                      <ContentPresenter x:Name="ContentHost" Margin="{TemplateBinding Padding}"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Grid>
                </ControlTemplate>
              </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="{x:Static GridView.GridViewStyleKey}"
               TargetType="{x:Type ListView}">
            <Setter Property="Background" Value="Lime"/>
        </Style>
        <Style x:Key="{x:Static GridView.GridViewItemContainerStyleKey}"
               TargetType="{x:Type ListViewItem}">
            <Setter Property="Background" Value="Yellow"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ListView x:Name="_listView1">
            <system:String>Item 1</system:String>
            <system:String>Item 2</system:String>
            <system:String>Item 3</system:String>
        </ListView>

        <ListView x:Name="_listView2" Grid.Column="1" 
                  Style="{StaticResource {x:Static GridView.GridViewStyleKey}}"
                  ItemContainerStyle="{StaticResource {x:Static GridView.GridViewItemContainerStyleKey}}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Date"/>
                    <GridViewColumn Header="Day of Week" DisplayMemberBinding="{Binding DayOfWeek}" />
                    <GridViewColumn Header="Year" DisplayMemberBinding="{Binding Year}" />
                </GridView>
            </ListView.View>
            <system:DateTime>1/1/2010</system:DateTime>
            <system:DateTime>1/1/2011</system:DateTime>
            <system:DateTime>1/1/2012</system:DateTime>
        </ListView>
    </Grid>
</Window>
هل كانت مفيدة؟

المحلول

I ran into the same odd/tricky problem as you're describing above, and ran into a blog post that suggested a neat little hack/fix which seems to provide the behaviour you're after. The main points are repeated in case the link dies.

You've already described the odd requirements for ListView styling if you want to cover views with and that make use of GridViews; a basic ListView needs a ContentPresenter, a GridView ListView needs a GridViewRowPresenter.

The poster managed to get around this by including both presenters within his style, and using a Setter to show the ContentPresenter only when required.

So your ControlTemplate could implement something along these lines (with your extra styling properties added as required):

<ControlTemplate TargetType="{x:Type ListBoxItem}">
    <!-- Pair of presenters -->
    <Grid>
        <GridViewRowPresenter x:Name="gridrowPresenter"
                Content="{TemplateBinding Property=ContentControl.Content}"/>
        <ContentPresenter x:Name="contentPresenter"
                Content="{TemplateBinding Property=ContentControl.Content}"  Visibility="Collapsed"/>
    </Grid>
    <!-- Visibility Controlling Setter -->
    <ControlTemplate.Triggers>
        <Trigger Property="GridView.ColumnCollection" Value="{x:Null}">
            <Setter TargetName="contentPresenter" Property="Visibility" Value="Visible"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

Both GridViewRowPresenter and ContentPresenter are present in the style, but the ContentPresenter is hidden (Visibility="Collapsed").

The neat trick is the use of a Trigger on GridView.ColumnCollection; if this value is null (which occurs when the GridViewRowPresenter has no content), the ContentPresenter will be made visible, correctly displaying your normal ListView content). The GridViewRowPresenter will have no content, so it won't display any conflicting visuals.

If the GridView has content, it will be displayed (providing the correct row formatting), and the ContentPresenter will remain hidden.

Original blog entry: http://www.steelyeyedview.com/2010/03/contentpresenter-gridviewrowpresenter.html

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top