WPF - scaling uniformtofill not working as expected - wpf

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.

Related

How to prevent Labels on bottom of window from disappearing when resizing?

Basically I have a window is that SizeToContent="WidthAndHeight" and I have a Label positioned on the bottom. When resizing the window the label disappears. Unless I make the window a lot bigger.
I have the following XAML code:
<Window x:Class="DateKeeper.View.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:DateKeeper.View"
mc:Ignorable="d"
Title="Test" SizeToContent="WidthAndHeight"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="label" Content="Label" HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Column="0" Grid.Row="1" FontSize="36"/>
<TextBlock x:Name="textBlock" Grid.Column="1" Text="TextBlock" VerticalAlignment="Bottom" Grid.Row="1" FontSize="36"/>
</Grid>
</Window>
And here are the pictures:
Before Resizing
After resizing
SizeToWidthAndHeight sets the window's initial size. It doesn't prevent the user from resizing the window.
ResizeMode="NoResize" will prevent all resizing. If you want to set a minimum size and allow the window to be sized larger than that, you can't do that in pure XAML.
However, you can use that initial size as a minimum width and height with some simple code in a loaded handler, like so:
<Window
x:Class="DateKeeper.View.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:DateKeeper.View"
mc:Ignorable="d"
Title="Test"
SizeToContent="WidthAndHeight"
Loaded="Window_Loaded"
>
Test.xaml.cs (or whatever you call the codebehind for the above Window):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MinWidth = ActualWidth;
MinHeight = ActualHeight;
}
You could also write an attached property that would do the same thing.
Update
This pure-XAML approach with Mode=OneTime seems promising:
<Window
...
SizeToContent="WidthAndHeight"
MinHeight="{Binding ActualHeight, RelativeSource={RelativeSource Self}, Mode=OneTime, PresentationTraceSources.TraceLevel=High}"
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Mode=OneTime}"
...
...but in trace output, we see that it gets the values of ActualHeight and ActualWidth very early on, before the window is sized to its content, perhaps before then actual UI window object is even created -- at a point when the actual width and height are both reported as zero.
You have 50% now
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="label" Content="Label" FontSize="36"
Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
<TextBlock x:Name="textBlock" Text="TextBlock" FontSize="36"
Grid.Row="1" Grid.Column="1" VerticalAlignment="Bottom"/>
</Grid>
Not sure what you are trying to create, but three things:
The elements are losing visibility because they are going off of the screen because they are running out of room
Your layout puts both text elements in the second row of the Grid
a. The elements will only have 50% of the available height to render
b. FontSize and Margins/Padding will further offset this
You have two different text element types (Label and TextBlock)
a. Both have different Margin/Padding values
b. The different layout values will result in different levels of visibility
If you don't want the elements to lose visibility, you need to set a minimum size of the Window (e.g. 325x150).

Scale images in listview to fit

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.

xaml layout introduction for Qt programmer

I am used to Qt and its Qt Designer.
XAML with its layout controls 'grid' and 'StackPanel' are somehow similar, but I still miss or do not find some of the most common properties of designs in Qt. Since I am completely new to XAML I would like to know the answers or further available documentations.
For example I want to add 2 elements (lets say buttons) either horizontal or vertical with their default height and a minimum height and minimum width. If aligned horizontal they shall be pushed to the left side and the remaining side on the right should be free. That means that the size of the buttons does not increase if the windows size is increased. In Qt this is realized by a grid in combination with a spacer (see for example this tutorial for an example).
My first XAML is far away from what I expect. The definition of RowDefinition is copied from a tutorial. However I do not understand its meaning...
<Window x:Class="SpectrumViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Spectrum Viewer" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="8" VerticalAlignment="Top">
<Button MinWidth="75" MaxHeight="23" Margin="10" HorizontalAlignment="Left" Width="100" Name="buttonTest">test</Button>
<TextBox MinWidth="75" MaxHeight="23" Margin="10" HorizontalAlignment="Left" Width="150" Name="textBoxValue"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="8" >
<Button MinWidth="75" MaxHeight="23" Margin="10" HorizontalAlignment="Left" Width="100" Name="button1">button 1</Button>
<Button MinWidth="75" MaxHeight="23" Margin="10" HorizontalAlignment="Left" Width="100" Name="button2">button 2</Button>
</StackPanel>
</Grid>
</Window>
The next image shows that the elements are pushed to the left side, but the two stacked panels above each other are not pushed to the top. If I set them both with VerticalAlignment="Top" they overlap which is also wrong.
If I resize the window one can see that the elements are not resized and that the second StackPanel overlaps with the first:
Instead the elements should be resized to the minimum width and any further resizing of the windows should be prohibited.
The first problem of the two stack panels overlapping when you set both VerticalAlignment to Top is because since you haven't specified in which Grid.Row they should be, the default would be both in Grid.Row = 0. Specify the row index and they won't overlap :
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="8" VerticalAlignment="Top">
....
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="8" VerticalAlignment="Top">
....
</StackPanel>
There is no way the window would stop resizing when the minimum size of the buttons is reached. You could set the MinWidth of the Grid and ask the window to stop right there :
<Window x:Class="SpectrumViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Spectrum Viewer" Height="350" Width="525"
MinWidth="{Binding RelativeSource={RelativeSource Self}, Path=Content.MinWidth}">
<Grid MinWidth="350">
.......
</Grid>
</Window>
Or set the MinWidth of the Window itself :)

Why is there no vertical scroll bar for this listbox

Given the following XMAL why is no vertical scrollbar for the ListBox which is bound to an ObservableCollection of 100 strings. If I change the height of the second row from * to something fixed like 500 then a scrollbar appears, but obviously i want the row height to be what ever is available (which is what I understand * to mean)
<UserControl x:Class="SimpleStack.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<DataTemplate x:Key="ListBoxItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Place holder"/><TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="Azure">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="The Text" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/>
<ListBox ItemsSource="{Binding ListOfNumbers}" Grid.Row="1" Grid.Column="0"
ItemTemplate="{StaticResource ListBoxItemTemplate}"/>
<TextBlock Text="Place Holder" Grid.Row="1" Grid.Column="1"/>
</Grid>
</UserControl>
The * row height is in fact "everything else available" (if you have multiple *s, it would divy that up). I am guessing that your actual issue is that "whatever is available" is infinite. Most likely the usercontrol is being given an unlimited amount of space, so it is expanding to take up as much room as it needs. Make sure you're limiting your usercontrol to the actual visible space and your listbox should get its scrollbar.
My understanding is that because of the measure/arrange layout system you are essentially telling the ListBox it can have all the vertical space it needs without being constrained. The internal ScrollViewer in the default ListBox template is therefore never constrained to trigger the scroll bar to appear.
I can see two ways to fix this for your situation:
-Specify ScrollViewer.VerticalScrollBarVisibility="Visible" on the ListBox to force the internal ScrollViewer to always show the scroll bar.
-Use an actual ScrollViewer to contain the ListBox and let that provide the scrolling capability instead of the one in the internal ListBox (you might have to tweak padding and borders to get it to look right):
<ScrollViewer Grid.Row="1" Grid.Column="0">
<ListBox ItemsSource="{Binding ListOfNumbers}"
ItemTemplate="{StaticResource ListBoxItemTemplate}"/>
</ScrollViewer>
I would prefer the second way because it would only show the vertical scroll bar if it was really necessary.

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