Scale images in listview to fit - wpf

I'm trying to make a listview where each item is an image. I want the listview to display the items horizontally. If the view box items won't fit horizontally in the window I want a horizontal scroll bar. If the list items won't fit vertically within the window I want the images to scale down so they fit. Instead of the images scaling to fit I seem to be getting a vertical scrollbar on the listview.
At the moment when the window is resized vertically it causes a vertical scrollbar to appear on the listview. I've tried various options around setting the image height to the height of the ancestor listview but I can't make it work correctly. How do I achieve my desired behaviour?
<Window x:Class="ViewBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ViewBoxExample"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainWindow}"
Title="Viewbox Test"
Height="400" Width="600">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate" >
<Border BorderBrush="Black" BorderThickness="2" >
<Image Margin="2" Source="image.png" />
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView VerticalAlignment="Stretch"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Items}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<Grid Grid.Row="1" Height="100">
<!--placeholder for more content-->
</Grid>
</Grid>
</Window>

Disable the vertical ScrollBar to make the ListView scale its items vertically:
<ListView ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource ItemTemplate}"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
This works as well with the more light-weight ListBox.

Related

WPF - scaling uniformtofill not working as expected

Been struggling with this for a while now. I still don't really get what's going on. Viewbox Scale="UniformToFill" is supposed to make the content stretch out until it fills the screen. In the following pictures. I'm using the same control. Yet I get different results. What I really want is both to look like img1. When I say look I mean I want both to scale to fit the screen and not go out of bounds like on img1.
img1 - correct one
img2 - wrong scaling
<UserControl x:Class="lou"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:lou.Controls"
xmlns:src="clr-namespace:lou"
mc:Ignorable="d"
x:Name="control" DataContext="{Binding RelativeSource={RelativeSource Self}}" MaxHeight="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=ActualHeight}">
<Viewbox Stretch="UniformToFill">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="SportLabel" Content="{Binding Path=SportName}" Foreground="Orange"/>
<ScrollViewer x:Name="SV1" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Hidden" >
<DataGrid Name="dataGrid" ItemsSource="{Binding DataTable}" Style="{StaticResource dataGrid}" ColumnHeaderStyle="{StaticResource DataGridColumnStyle}"/>
</ScrollViewer>
</Grid>
</Viewbox>
<Window x:Class="lou.test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:lou"
mc:Ignorable="d"
Title="asdfasdf" Height="300" Width="300"
WindowStyle="None"
WindowState="Normal"
x:Name="this" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="grid1" Background="Black">
<ScrollViewer x:Name="mainWindowScroller" VerticalScrollBarVisibility="Visible" CanContentScroll="True">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Collection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate x:Name="itemsPanelTemplate">
<StackPanel x:Name="stackPanel" Orientation="Vertical" IsItemsHost="True"
MouseLeftButtonDown="stackPanel_MouseLeftButtonDown" ScrollViewer.VerticalScrollBarVisibility="Hidden"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
UniformToFill will stretch input content into the output area by using the same horizontal and vertical scale ratios, and considering the top-left corner as origin point for the transform. Therefore, either the entire width or the entire height are matching the output area width or height (depending on size ratios of input and output).
You may want to use Fill if you want to stretch all input content to all output area, but it will use different size ratios for horizontal and vertical scales.
Another option is to use Uniform if you want to stretch using the same scale ratio and ensure all input is displayed in output area, but some margin may remain empty in the left-right or top-bottom sides of the output.
I tested with this XAML example (based on your question):
<Viewbox Stretch="UniformToFill">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="SportLabel" Content="Test" Foreground="Orange"/>
<ScrollViewer x:Name="SV1" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<DataGrid Name="dataGrid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}"/>
</DataGrid.Columns>
<DataGrid.Items>
<sys:String>Test1</sys:String>
<sys:String>Test2</sys:String>
<sys:String>Test3</sys:String>
<sys:String>Test4</sys:String>
</DataGrid.Items>
</DataGrid>
</ScrollViewer>
</Grid>
</Viewbox>
And checked the result in four ways:
If width of the container is large enough, with UniformToFill
If width of the container is smaller, with UniformToFill
If we use Fill instead of UniformToFill
If we use Uniform instead of UniformToFill
Test screenshots
I hope that by applying the same logic to your context will you will be able to understand the exact situation in your case, and you can choose the best solution for your needs.

How to resize UserControl

I have a UserControl in my WPF application. The UserControl has a listbox and could be any size, and underneath are some buttons.
When I place my UserControl in one of my Views, I would like it resize vertically as the parent resizes. BUT, I'd like the ListBox to decrease in heigh, not the entire control. This would mean on smaller screens, the buttons remain on screen but they don't see much content in the ListBox. I can use a MinHeight to ensure the buttons and at least 1 item in the listbox is visible!
I found similar question such as WPF user control does not resize with main window but my code doesn't have any height or width
My UserControl
<Grid>
<StackPanel>
<ListBox ItemsSource="{Binding MessageList" ></ListBox>
<WrapPanel HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</StackPanel>
</Grid>
And the View
<Grid>
<StackPanel>
<uc:RecentList MessageList="{Binding Messages}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StackPanel>
</Grid>
It doesn't matter how I resize my View, the UserControl appears to stay at the exact same size.
I tried the ViewBoxbut this was scaling everything!
I also tried (in the UserControl)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding MessageList, RelativeSource={RelativeSource AncestorType=UserControl}}" Grid.Row="0"></ListBox>
<WrapPanel Grid.Row="1" HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</Grid>
But no difference.
StackPanel does not stretch its child elements in the direction of its Orientation.
So remove all StackPanels and define two rows in the Grid in the UserControl:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding MessageList ...}"/>
<WrapPanel Grid.Row="1" HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</Grid>
In main view:
<Grid>
<uc:RecentList ... />
</Grid>
Take a look at the Panels Overview article on MSDN.

WrapPanel Horizontal orientation doesn't work

With new version of kinect not exist the old kinectScrollviewer so i have used a ScrollViewer with a listView of images. The problem is the unscrollable when hidden ScrollbarVisibility or with horizontal scroll and if i use a SelectionChanged it works fine with mouse, but if i use a hand gesture after first click the selection area not disappear and so i don't select the elements
I would scroll only horizontal (so i have disabled vertical), but also with your code doesn't scroll with gesture. Also the click doesn't works.
If i use orientation="Vertical" it's scroll vertical (although in the example scroll horizontal using this setting), but if i use orientation="Horizontal" it doesn't works :(
<k:KinectRegion x:Name="ChoiceExercise" Background="Black" >
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<k:KinectUserViewer Grid.Row="0" Height="100"/>
<ContentControl Grid.Row="1" x:Name="navigationRegion">
<Grid x:Name="kinectGrid">
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" k:KinectRegion.IsScrollInertiaEnabled="True">
<ListView Grid.Row="0" x:Name="listViewExercise" SelectionChanged="listViewExercise_SelectionChanged" BorderThickness="0" Background="Black" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</ScrollViewer>
</Grid>
</ContentControl>
</Grid>
</DockPanel>
</k:KinectRegion>
ListView already contains ScrollViewer as part of default template and it's this behaviour that you need to disable by setting attached ScrollViewer.VerticalScrollBarVisibility property to Disabled
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<k:KinectUserViewer Grid.Row="0" Height="100"/>
<ContentControl Grid.Row="1" x:Name="navigationRegion">
<Grid x:Name="kinectGrid">
<ListView
Grid.Row="0"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
x:Name="listViewExercise"
SelectionChanged="listViewExercise_SelectionChanged"
BorderThickness="0"
Background="Black" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</ContentControl>
</Grid>
Also WrapPanel with horizontal orientation is meant to stack items horizontally until item cannot fit then move to the next row. Since you you want to scroll horizontally I think horizontal StackPanel would suit you better.
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>

Using WrapPanel and ScrollViewer to give a multi-column Listbox in WPF

I’m making a simple LOB app which loads data from an XML file and displays it in a list with a few buttons for editing.
In my first attempt, everything was ok except that the list scrolled downwards in one long column. I would prefer the data to wrap so that at the bottom of the Window it starts a second column, and so on – if you resize the Window the data should resize accordingly.
First, I just put the ListBox inside a ScrollViewer. This made no difference whatsoever.
Then, I added a WrapPanel within the ItemTemplate. At this point I got a long row horizontally but it never wrapped to a second row, despite my setting the ScrollViewer.HorizontalScrollbar=disabled.
I’ve searched around the web on various blogs and forums, but can’t see the difference between the suggestions and my code (included below). Any tips would be much appreciated.
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My App" Height="300" Width="400"
FocusManager.FocusedElement="{Binding ElementName=eventsList}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ListBox Name="eventsList">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</ScrollViewer>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Center" Visibility="Collapsed">
<Button Name="action1Button" />
<Button Name="action2Button" />
<Button Name="action3Button" />
</StackPanel>
</Grid>
</Window>
It seems like you were on the right track: replacing the ItemsPanelTemplate in the ListBox with a WrapPanel, setting WrapPanel's Orientation to Vertical, and setting ScrollViewer.VerticalScrollBar to Disabled should be all you need to do.
This works for me:
<Window x:Class="ScrollingWrapPanel.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<ListBox ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Red"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Orange"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Yellow"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Green"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Blue"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Indigo"/>
</ListBoxItem>
<ListBoxItem>
<Rectangle Width="80" Height="80" Margin="10" Fill="Violet"/>
</ListBoxItem>
</ListBox>
</Grid>
</Window>
That should cause it to render out a full column vertically, wrap, and then continue on the next column, scrolling as necessary horizontally (but not vertically), as in the picture:
The key things in this implementation are
Setting Orientation="Vertical" on the WrapPanel so that things wrap vertically and not horizontally, and
Setting ScrollViewer.VerticalScrollBarVisibility="Disabled" on the ListBox so that the ScrollViewer knows to restrict its height to the available space.
I believe to do this, you need to write custom code - you've got the right idea in overriding ItemsPanelTemplate, but WrapPanel doesn't order stuff the way you want it - it'll order stuff as:
A B C D
E F G H
I J K L
Whereas you probably want it:
A D G J
B E H K
C F I L
Also, by putting it in a ScrollViewer, it's like telling it that it has an infinitely sized screen, so the result will just be one row (because the ScrollViewer will give it as much room as it asks for). Writing a panel isn't hard, it's basically just two functions (Measure and Arrange).

Centering a WPF control

I have a window where I add a new UserControl to (with an image), I simply want to center the control in the middle of the screen (both vertically and horizontally). I can only get the vertical one to work. I'm gonna swap content in the DockPanel from my CodeBehind and want to show this startup screen before I start doing my slideshow UI, this means that the content is set from the CodeBehind.
My Window:
<Window x:Class="GreenWebPlayerWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="512" Width="853" WindowStyle="None" WindowState="Maximized" WindowStartupLocation="CenterScreen">
<DockPanel Width="Auto" Height="Auto" Name="TransitionContainer" Background="Black" Margin="0" LastChildFill="True"></DockPanel>
</Window>
My UserControl:
<UserControl x:Class="GreenWebPlayerWPF.FrontPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel Background="Black">
<Image Name="image1" Stretch="None" Source="/GreenWebPlayerWPF;component/gw.png" />
</DockPanel>
</UserControl>
Please note that I'm using maximized/full screen.
Use a Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Replace with your UserControl -->
<Button Content="Foo" Grid.Column="1" Grid.Row="1"/>
</Grid>
You can dock it inside your DockPanel (if you must have a DockPanel there) to stretch. And, of course, while the above is all markup, you can just as easily create such a grid from code.
I keep running into this problem when trying to center elements on the page.
The problem with the StackPanel is that HorizontalAlignment has no effect when the Orientation is Horizontal and VerticalAlignment no effect when Orientation is Vertical. So you keep banging your head trying to set values with no effect. It is not illogical that it works this way but it would be good if this was reported as an error.
The solution I found is to have two imbricated StackPanels one centered horizontally and the other vertically as shown below. Finding the size of the parent is needed to size the intermediate panel otherwise it would be flat and its content hidden - an absolute value would work as well. Although not a panacea itis a bit less verbose than using a grid.
<StackPanel Background="Bisque" Orientation="Vertical" Width="300" Height="300" >
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel}, Path=ActualHeight}">
<StackPanel VerticalAlignment="Center" Width="200" Height="60" Background="Blue">
</StackPanel>
</StackPanel>
</StackPanel>
It's quite an old one, but centring a control is now as simple as:
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Height="350" Width="600">
<TextBox />
</StackPanel>
</Grid>

Resources