Adding labels on both ends of a progress bar in Xamarin Forms - xamarin

I have a progress bar in Xamarin Forms defined in my XAML.
Actually I want to add labels beneath the progress that shows the minimum and maximum value of the progress bar as shown in the image below:
My XAML code:
<StackLayout Orientation="Vertical" Margin="5">
<Frame Padding="10"
BackgroundColor="White"
HeightRequest="80">
<Frame.Content>
<Label Text="%" HorizontalTextAlignment="End" FontSize="Small"/>
<ProgressBar x:Name="myProgressBar" WidthRequest="100"
HeightRequest="15" VerticalOptions="Center" HorizontalOptions="Center" Progress="0.2"/>
</Frame.Content>
</Frame>
</StackLayout>
Can someone please help me to achieve this in Xamarin Forms ? Also, how can I add a gradient color like in the image ?

For the text, you would probably want to use labels positioned under the progressbar. I'd recommend a grid with two rows.
As for the gradient, unfortunately the xamarin forms progress bar doesn't support this out of the box. You can either create a custom renderer for each platform that draws the gradient, or consider a third party control like Syncfusion's (https://www.syncfusion.com/products/xamarin/progress-bar)

You can make a custom Xamarin Forms control.
The content could be something like that:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5* />
<ColumnDefinition Width="0.5* />
</Grid.ColumnDefinition>
<Image x:Name="gradientImage" Grid.Row="0" Grid.ColumnSpan="2" Margin="10,10,10,0" />
<Frame x:Name="progressFrame" Grid.Row="0" Width="{Binding Progress}" HorizontalOption="End" BackgroundColor="Gray" ... />
<!-- Labels -->
<Label x:Name="startLbl" Grid.Row="1" Grid.Column="0" Text="{Binding startLabel}" HorizontalOption="Start" />
<Label x:Name="endLbl" Grid.Row="1" Grid.Column="1" Text="{Binding endLabel}" HorizontalOption="End" />
</Grid>
For the gradient progress bar, instead of CustomRenderers you can use an image.
this image will be the full width gradient bar
over this image, like a MASK, there is a frame. You have to compute the frame width depending on the progress value. ==> It will 'make appear" the gradient image smotthly when progress will increase.
Hope you understand the concept :)
Then in the code behind (in your ViewModel ?) manage the start/end label values and the computation of the "progress" value... I presume you will have to make a Binding Converter for: "progress" ==> "bar Width"...
Tell me if it's clear

Wanted to comment on above answer from #KFactory but don't have permission yet.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5* />
<ColumnDefinition Width="0.5* />
</Grid.ColumnDefinition>
Has a few typos. missing end quotes and s on closing tag. Should be
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>

Related

Why an Entry control do not receive the input from user in Xamarin Forms?

enter image description hereI want to implement the xamarin essentials map, but with a floating search for the pins that will appear on this map. I'm using a grid to divide the grid's rows and thus use the first row for the map and the second to show a cardview related to the selected pin. I put the search control in row 0 so that it seems to float on the map.
I couldn't get the focus of the search control to work. I don't know if I'm implementing the InputTransparent property correctly.
Any ideas?
<Grid InputTransparent="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="140"/>
</Grid.RowDefinitions>
<maps:Map
x:Name="map"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
MapType="Street"
HasZoomEnabled="True"
IsShowingUser="True"
InputTransparent="False"
MoveToLastRegionOnLayoutChange="false"
>
</maps:Map>
<StackLayout Grid.Row="0" Grid.Column="0" InputTransparent="True">
<Frame
Margin="20,40,20,0"
Padding="0"
BorderColor="{StaticResource BlackColor}"
CornerRadius="8"
HeightRequest="35">
<Grid VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="35" />
</Grid.ColumnDefinitions>
<Image
Grid.Row="0"
Grid.Column="0"
HeightRequest="20"
HorizontalOptions="Center"
Source="ic_search_bar"
VerticalOptions="Center"
WidthRequest="20" />
<controls:CustomEntry
x:Name="entrySearch"
Grid.Row="0"
Grid.Column="1"
FontSize="14"
HeightRequest="35"
HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Start"
Keyboard="Text"
Placeholder="¿Qué se te antoja hoy?"
PlaceholderColor="#979797"
ReturnCommand="{Binding SearchCommand}"
InputTransparent="False"
ReturnType="Search"
Text="{Binding SearchText}"
TextChanged="entrySearch_TextChanged"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" />
<Image
Grid.Row="0"
Grid.Column="2"
HeightRequest="15"
HorizontalOptions="Center"
Source="ic_clear_search.png"
VerticalOptions="Center"
WidthRequest="15">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ClearSearchCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</Grid>
</Frame>
</StackLayout>
</Grid>
I try to use the property in many parts of the code
From the docs on InputTransparent
true if neither the element nor its children should receive input and
should, instead, pass inputs to the elements that are visually behind
the current visual element
Your stackLayout is transparent so it’s children, including the Entry, are also transparent
You might be thinking InputTransparent does the opposite of what it actually does. True makes an area effectively invisible to touch; touch goes through it.
If you want an element to be touchable, it has to be "False". BUT all its ancestors must also NOT BLOCK touch.
If you really mean "True", but you have children you want to click on, do this:
<SomeLayoutNameHere InputTransparent="True" CascadeInputTransparent="False" ...>
That allows children to specify whether they can receive touch (InputTransparent=False), or are invisible to touch (=True).
"False" is the default.

How to reliably get actual dimensions of a UWP Popup?

I have a UWP App which contains a Page which has 4 Popups defined on it. Each Popup is data-bound to a separate Boolean in the View Model. By manipulating the VM, I can make any of the 4 popus appear - only 1 at a time - maximum.
I would like to control where these Popups appear, so I've added "Opened" event handlers to each of them. The event handlers do the appropriate math to calculate Vertical & HorizontalOffset for the Popups.
This question, which was never answered is my problem as well. To explain it better, querying the popups ActualWidth/Height gives nonsense values. All of my popups are different sizes but all are landscape (width > height). I consistently get ActualWidth=310 & ActualHeight=779 for all of them.
By using the Popup's Child's dimensions (always a Grid) in the math, 2 of the 4 work consistently; the other 2 sometimes work, but usually not. EDIT: They fail because the Grid has no dimensions.
I'm assuming the Opened event is as late as possible to allow all Layout / Measures to occur. Short of hard-coding Heights & Widths on the Popups (one of which will have variable content), I've run out of stuff to try.
There's a lot of code, but they're virtually identical. This is one of the ones that works intermittently ...
<Popup x:Name="LoserTool"
IsOpen="{x:Bind vm.bLoseTool, Mode=OneWay}"
Opened="Popup_Opened"
>
<Grid x:Name="gLoserTool"
Background="{StaticResource TaipanHunter_Primary_Dark}"
BorderBrush="{StaticResource TaipanHunter_Primary_Light}"
BorderThickness="2"
Padding="20"
RowSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="1.5*" />
<RowDefinition Height="5*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0.2*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Oh No!"
Grid.Column="1"
Grid.Row="0"
Style="{StaticResource TaipanHunter_Title}"
/>
<TextBlock Text="Your tool broke!"
Grid.Column="1"
Grid.Row="1"
Style="{StaticResource Text_LightText}"
/>
<Button Content="Play Again"
Click="StartNewGame"
Grid.Column="1"
Grid.Row="2"
HorizontalAlignment="Center"
Style="{StaticResource Button_Base}"
/>
</Grid>
</Popup>

Xamarin Forms: Expand Button size in a Grid for larger Accessible Text

I am creating a search form with an Entry and a Button at the top. I have it all laid out well when the text is a normal size. I have implemented scaling fonts for accessibility, and now when the font is at the second to largest size the font is too big for the button and it cuts off the text.
Here is my current code. I don't require using a grid, it's just the only way I could get the entry control to fill out all of the extra space that the button isn't using. The width and height requests on the controls is the only way I could get them to look well on normal text sizing
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:BorderedEntry Grid.Column="0" x:Name="queryEntry" Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" HeightRequest="{OnPlatform Android=50, iOS=35}" Margin="10,15,5,5"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Grid.Column="1" Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0" WidthRequest="80"
HeightRequest="35" IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
</Grid>
Here is what it looks like on normal text sizing:
Here is what it looks like on the third to largest text size:
Here is what happens once I go to the second to, and largest text size:
And here is ideally what I would like it to look like at a minimum visual. I got this by setting the buttons width request to 100 instead of 80 (I ideally don't want this value as it doesn't look good on small text size):
The only solution I have came up with is keep track of what the text scaling is, and once it goes above 1.5, then I can increase the WidthRequest on the button control. Ideally I would just like the button to adjust to the width of the text.
EDIT 1:
I tried Jason's solution with this code here:
<StackLayout Orientation="Horizontal">
<controls:BorderedEntry Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" HeightRequest="{OnPlatform Android=50, iOS=40}" Margin="10,15,5,5"
HorizontalOptions="FillAndExpand"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0"
IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" VerticalOptions="Center"
Padding="20,0,20,0"/>
</StackLayout>
I had to leave the HeightRequest on the Entry else the entry would be a smaller height. Without the Padding on the Button it showed like so:
Adding the padding added some space to the button (I tried this instead of the WidthRequest):
So now it looks good on normal text, however once I start cranking up the text size in accessibility, it looks good on the 3rd to largest size (same as my original solution above):
But once I move it to the top two it starts pushing the button off the screen to the right:
Then I had the idea to go back to using the Grid and using Padding instead of the WidthRequest on the Button, and it displays correctly:
this works for me. As I change the FontSize of the button, the entry will adjust to accomodate
<StackLayout Padding="10,100,10,0" HeightRequest="50" Orientation="Horizontal" BackgroundColor="LightBlue">
<Entry HorizontalOptions="FillAndExpand" Placeholder="Search" />
<Button FontSize="80" Text="Find" />
</StackLayout>
After trying out Jason's suggested solution it still did not layout properly at the largest text accessibility settings (see my edits above in the original post). I tried putting a Padding="20,0,20,0" on the Button and removed the WidthRequest to add spacing around the Find text in the normal text size case and that seems to have allowed the Grid to auto-layout the width I also removed the HeightRequests on both controls. I don't like how the Find button looks as it is larger than the entry. Here's the final xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:BorderedEntry Grid.Column="0" Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" Margin="10,15,5,5"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Grid.Column="1" Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0"
IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" VerticalOptions="CenterAndExpand"
Padding="20,0,20,0"/>
</Grid>
With these visuals on normal text (iOS / Android):
And these visuals on the largest text size (iOS / Android):
To fix the issue where the Find button is taller than I really want it to be on the normal text, I added conditional Binding depending on what the font scaling is:
private void SetButtonBinding()
{
if (App.Current.CurrentFontScale < ScaleFontThreshold)
{
findButton.SetBinding(Button.HeightRequestProperty, nameof(FindViewModel.ButtonHeightRequest));
}
else
{
findButton.RemoveBinding(Button.HeightRequestProperty);
}
}
Which achieves these final visuals:
I wish there was a way to do this all through Xaml, but I can't seem to find the settings.

How to use an Image on multiple rows in a Grid properly in Xamarin Forms

I am trying to create a custom, material-style, card cell in my Xamarin Forms app. I have the appearance pretty much down, but I'm having problems with the image in it. I want it to touch the top and bottom edges, the left hand edge, and be a square sort of shape, while maintaining the aspect ratio. But right now, all I get is this small image that won't play ball:
(I've had to cover the company images with paint, but trust me that they're about that size).
Here's what I actually want (again, please excuse the paint job)
I'm using an image in a grid view, in the 1st column and spanning all 4 rows. I've tried all of the LayoutOptions, which I used to understand but now I'm second guessing myself. I've also tried putting the image in a StackLayout as I thought you can expand children of a Stacklayout, but still no dice. Here is my simplified Xaml right now:
<Frame CornerRadius="10"
Margin="10, 5"
IsClippedToBounds="True"
BackgroundColor="White">
<Grid BackgroundColor="White" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Image HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="AspectFill" Source="{Binding ImageSource}"/>
</StackLayout>
<Label Grid.Row="0" Grid.Column="1"
Text="{Binding Favourite.FavKindName}"
FontSize="{DynamicResource InfoLargerTextFontSize}"/>
<Image Grid.Row="1" Grid.Column="3" Grid.RowSpan="4" Source="Contact.png"
VerticalOptions="CenterAndExpand" >
<Image.GestureRecognizers>
<TapGestureRecognizer />
</Image.GestureRecognizers>
</Image>
</Grid>
</Frame>
What's more, you can probably tell I'm pretty clueless about the phone icon on the right. I want that to occupy the centre and be of a decent button size for the card cell.
I've spent hours trying to figure this out. What am I doing wrong?
Frame has a default Padding of 20. That's why you have those margins inside the frame.
<Frame Padding="0" // Here , set padding to 0, so you can fill all Frame space
CornerRadius="10"
Margin="10, 5"
IsClippedToBounds="True"
BackgroundColor="White">

Xamarin forms Grid sizing issue

I'm new to Xamarin so excuse me if this is very obvious.
I added a Grid to my ContentPage. I want the grid to occupy the entire ContentPage, which I assume is the full size of the device. In my case I'm testing an iPhone 6S simulator.
The first row should be 7.58% of the total height of the Grid and the last one should fill in the rest. I add the label to see where the bottom of the last row is and it shows all the on the top. It looks like it's ~7% from the top which means it's the bottom of the 1st row.
<Grid x:Name="layoutGrid" HorizontalOptions="Fill" VerticalOptions="Fill">
<Grid.RowDefinitions>
<RowDefinition Height=".0758*"/>
<!-- Logo -->
<!-- Remaining Space -->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".154*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width=".154*" />
</Grid.ColumnDefinitions>
<Image Aspect="Fill" Source="{local:ImageResource App1.Resources.background.png}" Grid.ColumnSpan="3" Grid.RowSpan="11"/>
<Image Aspect="AspectFit" x:Name="settingsImage" Grid.Column="2" Grid.Row="0" Source="{local:ImageResource App1.Resources.settings-17-xxl.png}" HorizontalOptions="End"/>
<Label x:Name="lblTest" Grid.Row="1" Text="Bottom" TextColor="White" VerticalOptions="End" HorizontalOptions="Center" FontSize="18"/>
It looks like you are placing two items in the same space, which you can not do in a grid. IOW the first image in the XAML (it seems you want this across the entire grid?) does not have a row and column set, but defaults to row/column 0. Then your column span is 3 and row span is 11(?). You only have two rows, so that makes no sense. Also you then can not place the second image in Row 0 column 2 as that is already occupied by the first image. You may want to set the first image as a page background. Or use an Absolute or Relative layout in which you can overlay items. A grid does not allow overlaid items as far as I know. Also you have your vertical options for the label set to "End" which will place it at the bottom of row 1, not the top. Try the following XAML to see if that is closer to what you want:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
x:Class="App1.MainPage"
BackgroundImage="{local:ImageResource App1.Resources.background.png}">
<Grid x:Name="layoutGrid" HorizontalOptions="Fill" VerticalOptions="Fill">
<Grid.RowDefinitions>
<RowDefinition Height=".0758*"/>
<!-- Logo -->
<!-- Remaining Space -->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".154*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width=".154*" />
</Grid.ColumnDefinitions>
<Image Aspect="AspectFit" x:Name="settingsImage" Grid.Column="2" Grid.Row="0" Source="{local:ImageResource App1.Resources.settings-17-xxl.png}" HorizontalOptions="End"/>
<Label x:Name="lblTest" Grid.Row="1" Text="Bottom" TextColor="White" VerticalOptions="Start" HorizontalOptions="Center" FontSize="18"/>
</Grid>
</ContentPage>

Resources