Sign in to follow this  
Side Winder

[.net] [C# WPF] WrapPanel Layout

Recommended Posts

Hey, I have a program that will have a WrapPanel containing loads of images. My issue is that all the images are different sizes so they appear all wonky in the GUI. Ideally, I'd want them to look something like this: So the outer box is some form of container that is always a fixed size of... I dunno, 300x300 just for arguments sake, and then the inner box is an image. Does anyone know how I can do this? What parent container I can put the image into if that's what it comes to?

Share this post


Link to post
Share on other sites
All you have to do is set a fixed size on the image and set Stretch to Uniform:

<WrapPanel>
<Image Width="100" Height="100" Stretch="Uniform" Source="pic1.png"/>
<Image Width="100" Height="100" Stretch="Uniform" Source="pic2.png"/>
<Image Width="100" Height="100" Stretch="Uniform" Source="pic3.png"/>
<Image Width="100" Height="100" Stretch="Uniform" Source="pic4.png"/>
</WrapPanel>

Share this post


Link to post
Share on other sites
No that doesn't work. The aspect ratios of images are all different and I don't want just one 100x100 image for all of them; they have to keep their aspect ratios, which is why there needs to be some form of parent container that IS a square, so it doesn't matter if the images have different aspect ratios, they will always be lined up. A portrait image will centre in the parent container (the square) with space either side of the image, and a landscape photo can fit in the parent container with space above and below.

Share this post


Link to post
Share on other sites
Did you try itachi's solution?
In his solution the Image control is the square box that is fixed to 100x100.
The image contained in it is set to uniform scale so it never messes up the aspect ratio and will automatically stretch to a maximum.

Share this post


Link to post
Share on other sites
Yeah, but I want to keep the aspect ratios as they are in the images. I could just look at what's greater, height or width, and set the size according to that, but then the layout is all wrong. If you look at the image in the OP, if the outer boxes weren't there, the distance between images would be totally different, they wouldn't be aligned and it'd look.. well.. not very nice.

I had an idea of putting the image as a child of a border, and then making the visibility of the border as hidden, but then apparently that makes the child hidden too... Don't suppose there's anyway around that?

Cheers for the help so far.

Share this post


Link to post
Share on other sites
The aspect ratios are kept in the proposed solution. That is what the stretch uniform does.

I think I understand what you want but I too think that you do not understand the consequences.

Please, to make sure that I understand you, using the OP's image draw EXACTLY what you want the UI to look like. Reading your last post suggests that you want something different that you put in the OP. If you can draw exactly what you want we might be able to help you.

Share this post


Link to post
Share on other sites
why would you put something inside a Border, set its Visibility to Hidden/Collapsed and expect the child to still be visible?
I suppose by "hiding a border but not its child" you mean setting BorderThickness="0"? A Border can have only one child, so what's the point of wrapping the Image in a Border in the first place when you don't want a Border.

If the reason that you want to put the Image in a container is that you want to put additional content there, like the picture title, you could use a DockPanel, Grid or whatever suits your purpose...

<DockPanel LastChildFill="True" Width="100" Height="100">
<TextBlock Text="Picture title" DockPanel.Dock="Bottom"/>
<Image Source="YourPic.png" Stretch="Uniform"/>
</DockPanel>

That's still exactly the same idea though. Either I completely misunderstand your intentions, or you just didn't bother to paste the Xaml I gave earlier into your project, because that will give you just what you asked for in your OP. You have to distinguish between the picture you want to show and the WPF Image element that will be showing your picture. The size of the Image element has nothing to do with how your picture is scaled, that's what Image.Stretch is for, as ernow already pointed out.

€ Rephrased first paragraph

[Edited by - itachi on February 12, 2010 3:38:30 AM]

Share this post


Link to post
Share on other sites
Sorry, I was doing it in the C#, rather than the XAML. I made an error with something else in the WrapPanel and it completely messed everything else up. So yes, it does work, thanks muchly, and I apologise that it took so long to get the message across :p

Share this post


Link to post
Share on other sites
I just remembered where I saw the whole border thing that I wanted - in Windows Explorer. When you click on an icon the background of the icon goes blue... And on highlight it's light blue. How would I go about doing something like that? Because obviously the background isn't always visible, whereas the icon would be.

Share this post


Link to post
Share on other sites
A little late but maybe it's still of use to you...

You would typically have an ItemsControl displaying your Images, let's say a ListBox, then you would create a style for your ListBoxItems that changes the background and border color when it's is selected or the mouse is hovering. Here's a complete sample of what I think you want:

Code behind:

public class ImageItem {
public ImageItem(string title, ImageSource source) {
this.Title = title;
this.Source = source;
}
public string Title { get; set; }
public ImageSource Source { get; set; }
}

public partial class Window1 : Window {
public Window1() {
InitializeComponent();
this.Images = new ObservableCollection<ImageItem>();

this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Hydrangeas.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Jellyfish.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Lighthouse.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Penguins.jpg")));
this.Images.Add(new ImageItem("pic0", this.GetImageSource(@"C:\Users\Public\Pictures\Sample Pictures\Tulips.jpg")));
}

public static readonly DependencyProperty ImagesProperty = DependencyProperty.Register(
"Images", typeof(ObservableCollection<ImageItem>), typeof(Window1)
);

public ObservableCollection<ImageItem> Images {
get { return base.GetValue(ImagesProperty) as ObservableCollection<ImageItem>; }
set { base.SetValue(ImagesProperty, value); }
}

BitmapImage GetImageSource(string path) {
BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = new Uri(path, UriKind.Absolute);
img.EndInit();
return img;
}
}



The Xaml could look like this:

<Window
x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication3"
Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">

<Window.Resources>
<style x:Key="ImageItemsstyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border CornerRadius="4"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#AAAF"/>
<Setter Property="Background" Value="#2AAF"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="#AAF"/>
<Setter Property="Background" Value="#5AAF"/>
</Trigger>
</style.Triggers>
</style>

<DataTemplate DataType="{x:Type l:ImageItem}">
<DockPanel Margin="4" LastChildFill="True">
<TextBlock Text="{Binding Title}" DockPanel.Dock="Bottom"/>
<Image Source="{Binding Source}" Width="50" Height="50"/>
</DockPanel>
</DataTemplate>

</Window.Resources>

<ListBox Margin="10" ItemsSource="{Binding Images}" ItemContainerstyle="{StaticResource ImageItemsstyle}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollContentPresenter}}}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

</Window>

There is a style for the ListBoxItem, a DataTemplate for the image-wrapper-class and finally the ListBox that displays it all. There might be an easier way to bind the WrapPanel's width but this works.

Share this post


Link to post
Share on other sites
I'm in the process of implementing that now but for some reason I can't add ImageItem as a custom type... I've added the namespace to the top of the XAML file and all it does is come up with an error saying "Cannot find the type custom:ImageItem. Note that type names are case sensitive". I've created the class, set it to public and it's definitely in the same namespace. Still nothing. What... Am I missing something here?

edit: ok it's now saying that the assembly is not found, and that I should verify that I am not missing an assembly reference... But looking in AssemblyInfo.cs I can clearly see that AssemblyTitle is exactly the same as what I've put in my XAML file. Sigh.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this