0

I have an ObservableCollection of BitmapSources and I want to display them all in a grid and override the selected and not selected styles. I have been looking at a lot of different ways to do this but have not managed to get it working to my satisfaction. My latest attempt looks something like this:

<ListBox ItemsSource={Binding Images}>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel ItemsHost="True">
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border BorderBrush="Black" BorderThickness="3">
                <Image Source={Binding}>
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

This does display the images, but the WrapPanel is always just one row... I don't want to have to scroll horizontally, so I want it to make row breaks by itself or be able to tell it that it should only have 3 items per row or something like that. Without the WrapPanel the images take one row each. Also, I don't really understand how to override the style for selected items and such since the DataTemplate's DataType is BitmapSource now, not ListBoxItem...

I have also tried a DataGrid (which seems more appropriate) with similar results.

How do I do this?

moggizx
  • 476
  • 1
  • 5
  • 19
  • 1
    For the styling look into [ItemContainerStyle](http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemcontainerstyle.aspx). – Clemens Oct 10 '13 at 09:30

3 Answers3

2

Define UniformGrid as your ItemsPanel and set the Columns value to the number of items you want to have per row.

          <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="3"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>

And your style to override default selection background and border brush setting:

           <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Style.Resources>
                        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                         Color="Transparent"/>
                    </Style.Resources>
                    <Setter Property="BorderThickness" Value="0.5"/>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="blue"/>
                        </Trigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="BorderBrush" Value="Red"/>
                        </Trigger>
                    </Style.Triggers>

                </Style>

            </ListBox.ItemContainerStyle>
Nitin
  • 18,344
  • 2
  • 36
  • 53
  • Awesome, this is exactly what I needed! And now, how do I override the ListBoxItem template? – moggizx Oct 10 '13 at 09:36
  • what exactly you want to override in template? – Nitin Oct 10 '13 at 09:43
  • As pointed out by others... the style of the ListBoxItems can be set using ListBox.ItemContainerStyle. – moggizx Oct 10 '13 at 09:48
  • yes..thats correct.. but you dont need to override the whole template just to set some properties – Nitin Oct 10 '13 at 09:50
  • Well, I want to change the mouseover and selection triggers (don't want the blue overlay thing, I want to change the BorderBrush instead, but it seems this is not available in the ItemsContainerStyle? :( – moggizx Oct 10 '13 at 09:52
  • you dont need to set borderthickness again in trigger... removing that – Nitin Oct 10 '13 at 11:51
  • Yeah, I removed it myself already :) But I have another issue: The border doesn't get the color I'm setting, it's always different shades of blue. I'm guessing because of some "overlay" effect in front of the border, so I've tried setting Foreground (and Background) to transparent, but it didn't help... Also, there's a margin between the image and the ListBoxItem's border, how do I make it stick to the edges of the image? I've tried setting the margin on the images to 0 but it didn't do anything... – moggizx Oct 10 '13 at 12:19
1

-Edit-
The part about listbox width in this answer is not correct, see clemens answer instead

-Original post-
You need to constrict the width of the list box, either by setting the width or with some other parent layout. Otherwise the wrap panel will claim all the available width to put everything on one row. This happens to me alot when using scrollpanes for example

One tip is to setting different background colors to elements to see who is actually grabbing the space

You can change the selected styles by setting the ItemsContainerStyle

aL3891
  • 6,205
  • 3
  • 33
  • 37
  • Nope, doesn't help... Restricting the ListBox's width (I tried Width="500") just seems to make the ScrollView's viewport smaller... Setting the width of the WrapPanel makes it enlarge every image to be that wide and then just stacks them on top of eachother. – moggizx Oct 10 '13 at 09:24
  • Where is the scrollview? Parented to the listbox? What happens if you set the width only on the scrollview? – aL3891 Oct 10 '13 at 09:28
  • I haven't defined a scrollview myself, I'm guessing it's part of the WrapPanel. Maybe I should've said the WrapPanel's viewport instead... sorry about that :P – moggizx Oct 10 '13 at 09:30
  • Ah, Its part of the listbox see clemens answer :) – aL3891 Oct 10 '13 at 09:33
  • Look into [ListBox Styles and Templates](http://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx). The ScrollViewer is part if the ListBox's ControlTemplate. – Clemens Oct 10 '13 at 09:36
1

You could simply disable horizontal scrolling. This will make the WrapPanel wrap its children.

<ListBox ItemsSource="{Binding Images}"
         ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border BorderBrush="Black" BorderThickness="3">
                <Image Stretch="None" Source="{Binding}"/>
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • This also causes it to expand every image to fill the entire width and then stacks them on top of eachother... – moggizx Oct 10 '13 at 09:32
  • I'm assuming `Image.Stretch` set to be `None`. See my edit. You may even set the image width or height to a fixed value. Then it would somehow look like a UniformGrid with wrapping instead of scaling. – Clemens Oct 10 '13 at 09:39
  • This works but it now wraps if the images don't fit by 1 pixel and I don't want to have to set the size of the ListBox so that the images fit perfectly, so I prefer nit's answer with the UniformGrid. – moggizx Oct 10 '13 at 09:45