alduin
alduin

Reputation: 317

How do I better stylize a ListBox of images in WPF?

I've binded a List from my code-behind to a ListBox but I'm having difficulty stylizing the look to get what I want. I'd like to show up to 8 images at once, but no more than that without scrolling down. When the window resizes, I would like the image sizes to scale with it but still have no more than 8 showing. Here's my current XAML:

<ListBox ItemsSource="{Binding PictureImagesList}">
    <ListBox.Template>
        <ControlTemplate TargetType="ListBox">
            <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
                <ItemsPresenter/>
            </ScrollViewer>
        </ControlTemplate>
    </ListBox.Template>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="4" HorizontalAlignment="Center" VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}" >
                        <Grid Background="{TemplateBinding Background}">
                            <Border HorizontalAlignment="Center" VerticalAlignment="Center"
                        BorderBrush="{TemplateBinding BorderBrush}">
                                <ContentPresenter />
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="BorderBrush" Value="Yellow" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

Here's a pic of what this XAML produces. As you can see the images are much too large and we only see the top half of the second row. If I mess around with ListBoxItem margin I can get them smaller but this isn't really ideal as it only works if the screen resolution stays the same.

I'm Getting This

Upvotes: 0

Views: 373

Answers (2)

Clemens
Clemens

Reputation: 128062

You could use a UniformGrid as ItemsPanel with appropriate HorizontalAlignment and VerticalAlignment. Also remove the redundant Border element from the DataTemplate.

<ListBox ItemsSource="{Binding PictureImagesList}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="4" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Image Width="200" Height="200" Margin="5" Source="{Binding}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Update: In order to have the yellow selection border directly around the image, use a ListBoxItem Style like shown below. To have the images scaled to (a fraction of) the full ListBox width, add an appropriate ControlTemplate.

<ListBox ItemsSource="{Binding PictureImagesList}">
    <ListBox.Template>
        <ControlTemplate TargetType="ListBox">
            <ScrollViewer HorizontalScrollBarVisibility="Disabled"
                          VerticalScrollBarVisibility="Auto">
                <ItemsPresenter/>
            </ScrollViewer>
        </ControlTemplate>
    </ListBox.Template>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="4" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Background" Value="LightGray" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}" >
                        <Grid Background="{TemplateBinding Background}">
                            <Border HorizontalAlignment="Center" VerticalAlignment="Center"
                                    BorderThickness="5"
                                    BorderBrush="{TemplateBinding BorderBrush}">
                                <ContentPresenter />
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="BorderBrush" Value="Yellow" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

Upvotes: 2

Mark Feldman
Mark Feldman

Reputation: 16119

Set your image dimensions to be the same and use a WrapPanel instead:

        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Border Margin="5" >
                        <Image Source="{Binding}" Stretch="Uniform" Width="400" Height="400"/>
                    </Border>
                </DataTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>

Alternatively, if you want a fixed number of columns then don't specify image dimensions at all and instead use a UniformGrid:

        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Border Margin="5" >
                        <Image Source="{Binding}" Stretch="Uniform" />
                    </Border>
                </DataTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="3" />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>

UPDATE: I'm at a bit of a loss now to understand exactly what it is you're trying to do, the images you're posting don't match your description. If you want the panels to be square, and the images to scale up to them uniformly with a thin border around them, then there are a few things you'll have to do:

1) change your ListBoxItem ControlTemplate to be a Border with a Transparent background and the ContentPresenter inside it. This will ensure that your yellow border doesn't fill the whole box, and that the rest of the box doesn't highlight when selected, but that you can still click anywhere on it to select it.

2) change your ItemTemplate to be a grid (so that it fills all available space) with a border centered in the middle of it with padding (so that you'll be able to see the yellow border when selected), then put your Image content inside that but wrap.

This should do the job:

<Style TargetType="{x:Type ListBox}" x:Key="PictureListBoxStyle">
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid Margin="5">
                    <Border Padding="5" HorizontalAlignment="Center" VerticalAlignment="Center">
                        <Border.Style>
                            <Style TargetType="Border">
                                <Setter Property="Background" Value="Transparent" />
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" Value="True">
                                        <Setter Property="Background" Value="Yellow" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Border.Style>
                        <Image Source="{Binding}" Stretch="Uniform" />
                    </Border>
                </Grid>
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <UniformGrid Columns="3" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
</Style>

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}" >
                <Border Background="Transparent">
                    <ContentPresenter  />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If that still isn't it then you'll need to define your requirements more clearly.

Upvotes: 2

Related Questions