SizeToContent not respecting SharedSizeGroups - c#

Using a window's SizeToContent="WidthAndHeight" property seems to break a grid's column and row definition's property SharedSizeGroup.
<Window x:Class="SizeTest.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"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
</Grid.RowDefinitions>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="0" Grid.Row="0" >
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="1" Grid.Row="0" >
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="0" Grid.Row="1" >
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="1" Grid.Row="1" >
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</Grid>
</Window>
For example, the code above produces the following:
However, I would expect it to produce similar to
Where it sizes to the components but respects the shared group size.
Resizing instantly applies the shared group sizes but then you have to manually guess where the correct size is.
Is there any way to fix this?
If it makes any difference, I am using .NET 5.
Edit
Adding Grid.IsSharedSizeScope="True" to the window or the grid initially appears to solve the issue, but when resizing the window, the grid no longer fills the window.
I suspect that what is happening is that without IsSharedSizeScope, the columns / rows are only the same size due to the Column / Row definition saying width / height is * but when IsSharedSizeScope is true, then the shared size scope come into effect, but for some reason stop the grid resizing when there is space for it to grow.

Looks like you need to use UniformGrid
<UniformGrid Rows="2" Columns="2">
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</UniformGrid>

Using a window's SizeToContent="WidthAndHeight" property seems to break a grid's column and row definition's property SharedSizeGroup.
No, in order to take advantage of shared size groups, you have to set a scope for the shared size groups to apply. That is what you do with the IsSharedSizeScope property attached property, which is false by default, which means initially there is no scope defined on any element, so the shared size groups will not apply.
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
</Grid.RowDefinitions>
<!-- ...other grid definitions. -->
</Grid>
For example, the code above produces the following: [...] Resizing instantly applies the shared group sizes but then you have to manually guess where the correct size is.
This is not related to shared size groups, but to the Grid itself. You can remove all the shared size definitions and it will yield the same result. In fact, the shared size definitions are redundant, as giving each column and cell a * size, will scale them in the same proportion. See the documentation for reference.
Star sizing is used to distribute available space by weighted proportions.
Unfortunately, I cannot tell why the inital size fits the Grid content exactly instead of respecting the star sizes. However, applying a MinWidth and MinHeight solves the issue, if applicable.
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="200" />
<ColumnDefinition Width="*" MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="200" />
<RowDefinition Height="*" MinHeight="200" />
</Grid.RowDefinitions>
<!-- ...other grid definitions. -->
</Grid>
This could be the same or a similar issue with star-sizing like in this related question.
[...] when IsSharedSizeScope is true, then the shared size scope come into effect, but for some reason stop the grid resizing when there is space for it to grow.
This is expected, since setting a shared size group will treat sizes differently.
Columns and rows that participate in size-sharing do not respect Star sizing. In the size-sharing scenario, Star sizing is treated as Auto.
Consequently, if you apply a *, the Grid will be measured and the size will remain even after resizing.
As you question suggests, you are dealing with uniform cells in your grid. In this case, you can circumvent these issues by simply using the UniformGrid control, which is dedicated for this scenario.
Provides a way to arrange content in a grid where all the cells in the grid have the same size.
<UniformGrid>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</UniformGrid>

Related

Why does the border stop rendering its minus margin while resizing the grid

I've used a border with negativ margin to mark a grid row. But I get a strange behaviour while resizing the window. Cutting the second column of the row makes the margin of the border disappear:
Of course this is a small example, in the main application I'm using a grid splitter, but the bahaviour stays the same. Is it possible to fix this somehow or is it a WPF bug?
MainWindow.xaml:
<Window x:Class="TestHighlightBorder.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:TestHighlightBorder"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Margin="100,0,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Border Background="DarkRed" Opacity="0.3" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="-80,0,0,0" Panel.ZIndex="1" />
<TextBlock Text="column 0" Background="LightBlue" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="column 2" Background="LightGreen" Grid.Row="0" Grid.Column="2" />
</Grid>
ps:
This is how the main application looks like. It's some sort of a propertygrid. The amount of the negativ margin depends on the level of nested objects.
I tried your example and it happens just as you said: column 2 gone, margin gone.
This seems to happen whenever the grid can't be displayed completely.
If, for example you set the third column definition to 200, the margin disappears as soon as column 2 isn't shown in it's entirety. Same thing happens when you resize the window from the bottom.
If you put the existing grid in another container (Grid, StackPanel, etc.) and set the MinWidth to something at least the size of the width of the columns + margin (in your example 310), this doesn't happen.
Like so:
<StackPanel MinWidth="310">
<Grid Margin="100,0,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Border Background="DarkRed" Opacity="0.3" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="-80,0,0,0" Panel.ZIndex="1" />
<TextBlock Text="column 0" Background="LightBlue" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="column 2" Background="LightGreen" Grid.Row="0" Grid.Column="2" />
</Grid>
</StackPanel>
Instead of adding a 100 left-margin to the grid, you could just fix a column at the beginning with the width of 100, set the column-span of the border to 4, replace the negative margin with 20 positive left-margin (100-80=20), and add 1 to the value of Grid.Column for each of your controls. So the final approach would look like that:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Border Background="DarkRed" Opacity="0.3" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Margin="20,0,0,0" Panel.ZIndex="1" />
<TextBlock Text="column 0" Background="LightBlue" Grid.Row="0" Grid.Column="1" />
<TextBlock Text="column 2" Background="LightGreen" Grid.Row="0" Grid.Column="3" />
</Grid>

UWP XAML: Filling the screen with 2 elements, one Viewbox

I have a grid with two elements, a scaling Viewbox and a Textblock. I want to have the Viewbox take only the space it needs, but also only the space it can get.
Images explain it much better, the desired image first:
However, when I resize my application to be wider, the Viewbox starts to overtake the Textblock below it:
Here's a dumbed down version of my XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="MainGrid" Margin="0" UseLayoutRounding="False">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" />
</Grid.ColumnDefinitions>
<Viewbox Stretch="Uniform" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" x:Name="Zulrah">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Fill="Blue" Width="150" Height="250" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<Rectangle Grid.Column="1" Fill="Red" Width="150" Height="250" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</Grid>
</Viewbox>
<TextBlock Grid.Row="1" x:Name="TextOutput" MinHeight="100" MinWidth="100">
Hello world!
<LineBreak />
Life's good
</TextBlock>
</Grid>
You can more or less ignore the second column (with Width="0"), it's used for when the application becomes wide-screen (or landscape). It has the same issue:
In short: I want the TextBlock to obey it's MinHeight="100", while still maximizing the space the Viewbox uses.
PS: Please note that some settings make the Viewbox scale to a larger size than actually fits on the screen, this is not desireable!
Edit: Remarkably, setting a MinHeight="100" on the second row has no effect...
Since you are using ThemeResource in your code, I think you are developing an UWP app as there is no ThemeResource in WPF. If so, please remove WPF in your title and tags as they are two different frameworks. Mixed use of UWP and WPF may cause confusion.
For UWP apps, in Grid, while setting row's height to Auto, the row will size to fit its content. After the Auto rows are calculated, the row which height is set to * will get part of the remaining height.
According to your description, you want the TextBlock to obey it's MinHeight and the Viewbox gets part of the remaining height. So you can change the RowDefinitions like following:
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
And to make the Viewbox fill the remaining area, we can set VerticalAlignment and HorizontalAlignment to Stretch. Besides this, you may also need to set Stretch property to Fill to make the content in Viewbox resize to fill the destination dimensions.
The complete XAML code may like following:
<Grid x:Name="MainGrid"
Margin="0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
UseLayoutRounding="False">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" />
</Grid.ColumnDefinitions>
<Viewbox x:Name="Zulrah"
Grid.Row="0"
AllowDrop="True"
Stretch="Fill">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
Width="150"
Height="250"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Blue" />
<Rectangle Grid.Column="1"
Width="150"
Height="250"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Red" />
</Grid>
</Viewbox>
<TextBlock x:Name="TextOutput"
Grid.Row="1"
MinWidth="100"
MinHeight="100">
Hello world!
<LineBreak />
Life's good
</TextBlock>
</Grid>

What kind of dynamic container and internal containers should I use for this intended view?

I want to make a popup which has a person's details on it. Each detail will be stacked vertically in the popup. I have two questions.
(1) How should I deal (graphically) with details which are not available?
(2) How to make the container around all the details dynamic so that its height is determined by the number of details available.
My first thought was the following;
<StackPanel Width="400"
Height="500">
<StackPanel x:Name="sp">
<Grid x:Name="spTelephone" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="1"
Margin="5,0,0,0">
<TextBlock Text="+Some Phone No."
VerticalAlignment="Center"
FontFamily="Verdana"/>
</Grid>
<Grid Grid.Column="2">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</Grid>
<Grid x:Name="spMobile" Height="50">
<!-- Repeat of above -->
</Grid>
<Grid x:Name="spEmail" Height="50">
<!-- Repeat of above -->
</Grid>
<!-- Further Grids -->
</StackPanel>
</StackPanel>
The idea being that if a detail is not available then I would set the Visibility property of the GRID to Visibility.Collapsed.
For example see my image with 3 details.
Then if a cell phone is not available it would look like this.
So how should I do this? I could for imagine using a ListView as well maybe as this would then take away the need to collapse the views. I could add each detail to an Item. But then how do I get the list view and its parent to resize its height?
Define datatemplate for item and use any items control to represent them.
Simple solution would be something like this:
Template
<DataTemplate x:Key="MyItemTemplate">
<Grid x:Name="spTelephone" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="1"
Margin="5,0,0,0">
<TextBlock Text="+Some Phone No."
VerticalAlignment="Center"
FontFamily="Verdana"/>
</Grid>
<Grid Grid.Column="2">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</Grid>
</DataTemplate>
Control
<Border BorderBrush="Black" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center">
<ItemsControl ItemsSource="{Binding ItemsCollection}"
ItemTemplate="{StaticResource MyItemTemplate}"/>
</Border>
Of course you must fill collection from dataContext and for Text="+Some Phone No." also use data binding from current collection item.(Use DataContext={Binding} in template)
Border here is just to show that ItemsControl size changes according to collection-items count. Also ItemsControl can be replaced with any of it's descendants.

WPF - Canvas Background Not Displaying

I am a complete newbie to WPF. I've been working on a personal project for about 2 weeks. It's been coming along, and suddenly hit a roadblock. Everything was working perfect until I shifted some things around.
It's basically a satellite map as the canvas background, so that I can overlay geometry on it. The image is 10000x10000, and has not changed. It's added as a resource and... funny enough, shows up in the Visual Basic xaml designer.
The local:ZoomBorder class zooms/pans the viewbox/canvas. I did not post the code because it has not been touched since it last worked.
Geometry is being added to the canvas correctly.
I moved around some dimensions, as far as the grid goes. Like adding margins and such, but can't get it back to displaying the imagebrush background, no matter what I do.
<Window x:Class="Temp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Temp"
Title="MainWindow" Height="930" Width="1100" PreviewKeyDown="mapBorder_PreviewKeyDown">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="210" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="2" Grid.Column="0">
<StackPanel Name="stackPanel" />
</ScrollViewer>
<local:ZoomBorder x:Name="mapBorder" Background="Gray" Margin="0" Grid.Column="1" Grid.Row="2" ClipToBounds="True">
<Viewbox>
<Canvas Name="mapGrid" Height="10000" Width="10000">
<Canvas.Background>
<ImageBrush ImageSource="map.jpg"/>
</Canvas.Background>
</Canvas>
</Viewbox>
</local:ZoomBorder>
<Button Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" MinWidth="80" Margin="10,0,0,0" Content="Start Refresh" Width="80" Click="buttonStartRefresh" />
<Button Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" MinWidth="80" Margin="110,0,0,0" Content="Stop Refresh" Width="80" Click="buttonStopRefresh" />
<CheckBox Name="Advanced" Grid.Column="1" Grid.Row="1" Margin="10,0,0,0">
<Label Margin="0,-5,0,0">Advanced</Label>
</CheckBox>
</Grid>
</Window>

SizeToContent Fills Screen with RichTextBox and FlowDocumentScrollViewer

I'm trying to show a FlowDocument in a WPF form and have tried both RichTextBox and FlowDocumentScrollViewer. I also require that the window resizes to be able to show all text.
Unfortunately, when I set SizeToContent="WidthAndHeight" for the Window itself, no matter what content I put in the FlowDocument, the window expands to the full width of all my displays! The height seems to resize fine, however.
Anyone know how to get it to resize properly? Looked all over and cannot figure out how to get this going...
XAML below:
<Window x:Class="CustomControls.SecureConfirmationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecureConfirmationDialog"
MinHeight="120" MinWidth="200"
Height="120" Width="300"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStyle="ToolWindow"
Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FlowDocumentScrollViewer Name="flowMsg" Grid.Row="0" Grid.ColumnSpan="3" Margin="3" IsToolBarVisible="False" ScrollViewer.VerticalScrollBarVisibility="Hidden" />
<TextBox Name="txtConfirm" Grid.Row="1" Grid.Column="0" Text="Testing" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="3" />
<Button Name="btnOK" Grid.Row="1" Grid.Column="1" Content="OK" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3" Width="50" Click="btnOK_Click" />
<Button Name="btnCancel" Grid.Row="1" Grid.Column="2" Content="Cancel" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3" Width="50" Click="btnCancel_Click" />
</Grid>
</Window>
SizeToContent only "works" if the content is actually bounded, in this case however the Grid, which is the content of Window, has no size restrictions so it will try to get all the space it can get, the window responds by giving it as much space as fits the screen.
If you want to prevent this you would need to make the container for your document to size to their content which might be impossible if the document does not have any bounds itself and also behaves in a give-me-all-you-have manner.

Categories

Resources