Force a view to be square in shape - xaml

I am trying to create a UI in XAML for Xamarin Forms in which I have a frame (though I assume the answer would be the same for any type of view) inside of a grid. I want the frame to use full width available to it in the grid cell that it occupies, but I want to force it to be square in shape. So, I want the width to size automatically, and have the height set to match its actual width.
Here's what I have so far:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="13*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="1"/>
<Button Grid.Row="1" Grid.Column="1"/>
<Button Grid.Row="2" Grid.Column="1"/>
<Frame Grid.Row="0" Grid.Column="3" Grid.RowSpan="3" OutlineColor="Silver" HorizontalOptions="Fill">
<Image />
</Frame>
</Grid>
Currently, the frame correctly fills the available width, but the height is about the same as the total height of the 3 buttons to the left of it combined.
I was thinking that I maybe needed to bind the frame's HeightRequest to its actual width, but I don't know if that's possible, or how to do it if it is. If not, any other options?

Since you're setting the RowSpan of the frame to 3, even if you explicitly set the frame's HeightRequest to some small value, the frame will still take up the entire grid's height. So first you have to prevent that from happening. You could do that by containing the frame inside some other view.
Now to make the frame's height and width the same as it scales, you could use the container's SizeChanged event and set the frame's height and width requests accordingly.
Also note that Frame by default has a padding of 20.
Here's the sample code for the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="13*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="1"/>
<Button Grid.Row="1" Grid.Column="1"/>
<Button Grid.Row="2" Grid.Column="1"/>
<StackLayout x:Name="frmContainer" Grid.Row="0" Grid.Column="3" Grid.RowSpan="3" BackgroundColor="Orange">
<Frame x:Name="frm" OutlineColor="Silver" VerticalOptions="Center" HorizontalOptions="Center" Padding="0" BackgroundColor="Green"/>
</StackLayout>
</Grid>
And here's the constructor in the code-behind:
InitializeComponent();
frmContainer.SizeChanged += (s, e) =>
{
frm.HeightRequest = frmContainer.Width;
if (frmContainer.Width > frmContainer.Height)
{
frm.WidthRequest = frmContainer.Height;
}
else
{
frm.WidthRequest = frmContainer.Width;
}
};

Related

Why I cannot set canvas's width via binding method?

My UI elements' layout (nest relation) is:
toolbarGrid -> toolbarCanvas -> toolbarPanel -> some buttons
My purpose is to make animation of those buttons.
The issue is that I use Debug.Write() to monitor the Width and ActualWidth parameters of the UI elements. And I found that when App screen size is changed, only toolbarGrid size is changed accordingly. While the other UI elements' sizes are not changed at all, although I use Binding method to bind their Width and ActualWidth to toolbarGrid.
So how can I set their Width and ActualWidth so that they could change according to App screen's real size?
toolbarPanel width = 1920, actualWidth = 1920
toolbarGrid width = NaN, actualWidth = 1034
toolbarBackground width = 1920, actualWidth = 1920
toolbarCanvas width = 1920, actualWidth = 1920
<Grid x:Name="gridTotal">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="recTangle1" Grid.Column="0" Grid.Row="0"/>
<Rectangle x:Name="recTangle2" Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="12"/>
<Grid x:Name="toolbarGrid" Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="12">
<Canvas x:Name="toolbarCanvas" Width="{Binding ElementName=toolbarGrid, Path=ActualWidth}">
<RelativePanel x:Name="toolbarPanel" Canvas.Top="0" Width="{Binding ElementName=toolbarGrid, Path=ActualWidth}">
<Image x:Name="toolbarBackground" Source="ms-appx:///Assets/MainPage/toolbar/toolbar-background2.png"
Width="{Binding ElementName=toolbarGrid, Path=ActualWidth}"
Height="{Binding ElementName=toolbarGrid, Path=ActualHeight}" Stretch="Fill" />
<Viewbox x:Name="vb2" Stretch="Uniform" RelativePanel.AlignHorizontalCenterWithPanel="True" Margin="20,0,20,0"
Height="{Binding ElementName=recTangle1, Path=ActualHeight}"
Width="{Binding ElementName=recTangle1, Path=ActualWidth}">
<Button x:Name="toolbarHistory" Style="{StaticResource toolbarImageStyle}">
<Image Source="ms-appx:///Assets/MainPage/toolbar/toolbar-history.png" Stretch="UniformToFill" />
</Button>
</Viewbox>
</RelativePanel>
</Canvas>
</Grid>
</Grid>
I tried to understand your problem. If the problem was that u like to Elements follow actual screen size this one works. I changed the XAML code and hoping that next solution works fine for you. I rid of the Canvas because that element cause the problem. Anyway, in this example u do not need it (unless u have planned some other use later). And also to set Row/Column Width/Height values is unneeded. Bindings are also unnessessary.
<Grid x:Name="gridTotal">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle x:Name="recTangle1" Grid.Column="0" Grid.Row="0"/>
<Rectangle x:Name="recTangle2" Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="12"/>
<Grid x:Name="toolbarGrid" Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="12">
<RelativePanel x:Name="toolbarPanel" Canvas.Top="0">
<Image x:Name="toolbarBackground" Source="ms-appx:///Assets/MainPage/toolbar/toolbar-background2.png" Stretch="Fill" />
<Viewbox x:Name="vb2" Stretch="Uniform" RelativePanel.AlignHorizontalCenterWithPanel="True" Margin="20,0,20,0">
<Button x:Name="toolbarHistory">
<Image Source="ms-appx:///Assets/MainPage/toolbar/toolbar-history.png" Stretch="UniformToFill" />
</Button>
</Viewbox>
</RelativePanel>
</Grid>
</Grid>

Xamarin Xaml Bind height of button to height of Label

I got a label and a button next to each other in a grid. I'm trying to get the button to match its Height with the Label.
current XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- vessel label and button -->
<Label StyleClass="headerSub" Grid.Column="0" x:Name="currentVesselLabel"
Text="huidig voertuig:"/>
<Button Grid.Column="1" Text="selecteer voertuig"
BindingContext="{x:Reference currentVesselLabel}"
HeightRequest="{Binding Path=HeightRequest}"
ClassId="selectVesselButton" x:Name="selectVesselButton"/>
</Grid>
I tried setting the BindingContext to the label and bind the HeightRequest property of the button to the Label's Height and HeightRequest property. However, both don't work. How do I bind the height of the button to the height of the Label?
Cause:
It seems that you forgot to set the height of row.When there is only one row in the grid.The default height of row will be set as the height of screen.
Solution:
Set the height of the row.Refer to the following code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- vessel label and button -->
<Label StyleClass="headerSub" Grid.Column="0" x:Name="currentVesselLabel"
Text="huidig voertuig:" VerticalTextAlignment="Center" HeightRequest="60"/>
<Button Grid.Column="1" Text="selecteer voertuig"
BindingContext="{x:Reference currentVesselLabel}"
HeightRequest="{Binding Path=HeightRequest}"
ClassId="selectVesselButton" x:Name="selectVesselButton"/>
</Grid>

Image flickers in ListView datatemplate

could someone tell me, how can I avoid image flickering in Datatemplate? Everytime I change the source through binding (MVVM pattern), the image in ListViewItem flickers. For other images in app I've used ImageOpened event in code-behind. But I can't be used when I have DataTemplate.
EDIT:
DataTemplate:
<DataTemplate x:Key="ContactItemDataTemplate" x:DataType="contactData:Contact">
<Grid MinHeight="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" x:Name="MainImage" Source="{x:Bind ImageStatusUri, Mode=OneWay}" Margin="0,8,12,8">
</Image>
<TextBlock Grid.Column="1" Text="{x:Bind Nickname, Mode=OneWay}" Foreground="Black" TextAlignment="Center"
VerticalAlignment="Center" FontSize="15" HorizontalAlignment="Left"/>
</Grid>
<Rectangle Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" StrokeThickness="0.4" Height="0.4"
VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Stroke="#D1D3D4"/>
</Grid>
</DataTemplate>
type Contact implements INotifyPropertyChanged.
EDIT2:
WriteableBitmap is working, but I have 3 images with scaling factors 100, 200 and 400. It always choose the image with scale 400. When I use normal binding no WriteableBitmap, it take image with scale 100.
The best solution here is for you to sub-class the Image control, overriding the Source property and controlling the transition from one image to another with a flicker-less flow. Learn here: https://mva.microsoft.com/en-US/training-courses/xaml-for-windows-10-controls-14482

Making fixed-width grid columns and rows in UWP

I am making an application and I want certain grid rows and columns to be fixed, while others resize to fit the window. My problem is that I cannot do this in Universal Apps.
In WPF, the solution is simple:
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="50" /> <!-- This row is a fixed height -->
<RowDefinition Height="*" MinHeight="200" /> <!-- This row is resizeable, but has a minimum height -->
<RowDefinition Height="100" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" /> <!-- This column has a fixed width -->
<ColumnDefinition Width="*" MinWidth="300" /> <!-- These rows are resizeable, but have minimum widths -->
<ColumnDefinition Width="*" MinWidth="300" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
</Grid>
When I try this in UWP, the rows and columns with fixed sizes resize while the others with asterisks stay fixed. I tried putting asterisks on the fixed rows and columns and removing the pre-existing ones. I thought that in UWP it was reversed, however this severely messed up my app and made it worse.
My solution was to try the following in UWP:
<Grid x:Name="pageGrid"
Background="White">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="20"
MinHeight="20"/>
<RowDefinition Height="*"/>
<RowDefinition MaxHeight="20"
MinHeight="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="20"
MinWidth="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition MaxWidth="260"
MinWidth="260"/>
<ColumnDefinition MaxWidth="20"
MinWidth="20"/>
</Grid.ColumnDefinitions>
</Grid>
The idea here is to have fixed margins around my controls, at 20 pixels width. Inside these margins there are two boxes: One has a fixed width and resizable height, and the other resizes in both directions.
Despite this, I again experienced the same problem where the margins resize but the 'resizable' boxes do not.
Is there actually a way to have fixed and resizeable rows and columns in a grid using Universal Windows Platform? So far, i have yet to find evidence of this.
Complete code:
<Page
x:Class="UniversalCamera.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UniversalCamera"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
MinWidth="800"
MinHeight="450"
Width="800"
Height="450">
<Grid x:Name="pageGrid"
Background="White">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="20"
MinHeight="20"/>
<RowDefinition Height="*"/>
<RowDefinition MaxHeight="20"
MinHeight="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="20"
MinWidth="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition MaxWidth="260"
MinWidth="260"/>
<ColumnDefinition MaxWidth="20"
MinWidth="20"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="2"
BorderBrush="Black"
CornerRadius="5"
Grid.Column="1"
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="-10,-10,-10,-10"/>
<Border BorderThickness="2"
BorderBrush="Black"
CornerRadius="5"
Grid.Column="1"
Grid.Row="1"
Margin="2,2,2,2">
<Image x:Name="imageFrame"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Border>
<Canvas x:Name="controlCanvas"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="2"
Grid.Row="1">
<StackPanel x:Name="controlStack"
Canvas.Top="0"
Canvas.Left="0"
Width="260"
Orientation="Vertical">
<Button x:Name="startLiveButton"
Width="200"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Content="Start Live"
Click="startLiveButton_Click"/>
<Button x:Name="stopLiveButton"
Width="200"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Content="Stop Live"
Click="stopLiveButton_Click"/>
<Button x:Name="freezeVideoButton"
Width="200"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Content="Freeze Video"
Click="freezeVideoButton_Click"/>
<Button x:Name="loadParameterButton"
Width="200"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Content="Load Parameter"
Click="loadParameterButton_Click"/>
<CheckBox x:Name="autoWhiteCheckbox"
HorizontalAlignment="Center"
Width="200"
Margin="0,25,0,0"
Content="Auto White Balance"
Checked="autoWhiteCheckbox_Checked"
Unchecked="autoWhiteCheckbox_Unchecked"/>
<CheckBox x:Name="autoGainCheckbox"
HorizontalAlignment="Center"
Width="200"
Margin="0,5,0,0"
Content="Auto Gain Balance"
Checked="autoGainCheckbox_Checked"
Unchecked="autoGainCheckbox_Unchecked"/>
</StackPanel>
</Canvas>
</Grid>
</Grid>
</Page>
This code is intended to have extra rows and columns as margins around the main controls. These should be fixed at 20 pixels. When I run the code the margins stretch and the central boxes stay fixed; this is the opposite of what I intended:
(The black outlined area stays the same size when the window is resized while the margins stretch to fit the window.)
Your main Grid is fixed at 800 x 450 px. If you remove that restriction, the grid will stretch appropriately
Updated code:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
MinWidth="800"
MinHeight="450">
<Grid x:Name="pageGrid"
...

How can I design individually the Border-Sides in XAML

How can I design the Border only Top, Left, Right or Bottom Side in XAMl?
In CSS is this possible with Border-Top:...
The border thicknes is a composite property of the left, top, right and bottom thicknesses (notice the difference in order from CSS). If you specify only one value you set all of them, but you can specify them separately:
BorderThickness="1,2,3,4"
In XAML you don't have border property on elements like you have in CSS. However, you can use a <Border> element and set individual thicknesses just as you can i CSS (sets left-right and top-bottom border thickness):
<Border BorderBrush="Blue" BorderThickness="2,4">
<TextBlock Text="Inside border"/>
</Border>
or (sets left, top, right, bottom thickness):
<Border BorderBrush="Blue" BorderThickness="1,2,3,4">
<TextBlock Text="Inside border"/>
</Border>
If you need more control of the border you can use a panel for layout. E.g. using a <Grid>:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" BorderBrush="Blue" BorderThickness="2"/>
<Border Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" BorderBrush="Green" BorderThickness="4"/>
<Border Grid.Row="1" Grid.Column="0" BorderBrush="Red" BorderThickness="3"/>
<Border Grid.Row="1" Grid.Column="2" BorderBrush="Red" BorderThickness="3"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Inside border"/>
</Grid>
You are free to put other visual elements in the grid cells.