I am currently working on an app to retrieve data from an SQL database and present it in the UI. I got the whole functionality runnig smoothly but now I'm stuck at der GUI part. I want the UI to adjust to the window size. The elements (img, labels, textboxes) have a minheight and minwidth but can also grow to the maximum available space. If the window gets too small, I want the UI to adjust like a responsive website.
The maximized window would like something like this: Maximized window
The window width got to small & the elements adjust accordingly: smaller window
My best approach was:
<Grid DataContext="{Binding CurrentPerson}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Person.Photo}"/>
</Grid>
<Viewbox Grid.Column="1" StretchDirection="Both" Stretch="Uniform" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" VerticalAlignment="Center">Title:</Label>
<Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">Name:</Label>
<Label Grid.Column="0" Grid.Row="2" VerticalAlignment="Center">Street:</Label>
<Label Grid.Column="0" Grid.Row="3" VerticalAlignment="Center">City:</Label>
<Label Grid.Column="0" Grid.Row="4" VerticalAlignment="Center">Number:</Label>
<TextBox Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="80"Text="{Binding Person.Title}"/>
<TextBox Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300">
<TextBox.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Person.LastName"/>
<Binding Path="Person.FirstName"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBox Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.Street}"/>
<TextBox Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="300" Text="{Binding Person.City}"/>
<TextBox Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" IsReadOnly="True" MinWidth="30" Text="{Binding Person.Number}"/>
</Grid>
</Viewbox>
</Grid>
The problem with this solution is that when the window gets too small, the content just shrinks to fit inside the window and isn't readable anymore. If the image could move above the person data it would save a lot of space an the person data could be readable.
I played around with wrappanel, viewbox, grid, uniformgrid and so on but I couldn't get it to work the way I want it to.
Any help is very much appreciated.
Thanks in advance!
This is going to involve some kind of C# code. You could write triggers that change Grid.Row
and Grid.Column
values on controls and use a value converter to decide when, and but this is simpler.
First, break down your main grid into two separate grids. You've got two panes, here, basically, so get their content in separate grids.
<StackPanel x:Name="MainLayout" Orientation="Horizontal">
<Grid>
<!-- img -->
</Grid>
<Grid>
<Viewbox Stretch="Uniform">
<!-- Title, name, etc. -->
</Viewbox>
</Grid>
</StackPanel>
Give the Window a SizeChanged
handler:
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActualWidth < 400)
{
MainLayout.Orientation = Orientation.Vertical;
}
else
{
MainLayout.Orientation = Orientation.Horizontal;
}
}
This can also be done with a UniformGrid
, if you prefer.
<UniformGrid x:Name="MainLayout" Columns="2">
<Grid
HorizontalAlignment="Left"
VerticalAlignment="Top"
>
<!-- img -->
</Grid>
<Viewbox
Stretch="Uniform"
HorizontalAlignment="Left"
>
<!-- Title, name, etc. -->
</Viewbox>
</UniformGrid>
Code behind
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActualWidth < 400)
{
//MainLayout.Orientation = Orientation.Vertical;
MainLayout.Columns = 1;
}
else
{
//MainLayout.Orientation = Orientation.Horizontal;
MainLayout.Columns = 2;
}
}
You can also switch grid column and row on the right pane:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
HorizontalAlignment="Left"
VerticalAlignment="Top"
>
<!-- Img -->
</Grid>
<Viewbox
x:Name="RightPane"
Grid.Column="1"
Grid.Row="0"
Stretch="Uniform"
HorizontalAlignment="Left">
<StackPanel
Orientation="Vertical"
>
<!-- Title, name, etc. -->
</StackPanel>
</Viewbox>
</Grid>
Code behind:
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActualWidth < 400)
{
//MainLayout.Orientation = Orientation.Vertical;
//MainLayout.Columns = 1;
Grid.SetColumn(RightPane, 0);
Grid.SetRow(RightPane, 1);
}
else
{
//MainLayout.Orientation = Orientation.Horizontal;
//MainLayout.Columns = 2;
Grid.SetColumn(RightPane, 1);
Grid.SetRow(RightPane, 0);
}
}
I'd like to urge you to consider not using the Viewbox
. Scaling fonts and controls to the window is unusual and not generally considered very usable. But it's your project.
If you do want to use the Viewbox
, read up about its Stretch
property, which governs how it scales its contents.
Have a look at ViewBox.StretchDirection
as well.