Xamarin forms - Create complex custom control (create multiple traffic signs) - user-interface

I have the following screen in an Android project:
How can I create controls like this in Xamarin forms? (I really don't want to implement it native).

You could use Frame and set the Padding and CornerRadius .
For example
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Frame BackgroundColor="Red" CornerRadius="10" WidthRequest="100" HeightRequest="50" Padding="5">
<Frame BackgroundColor="White" CornerRadius="8" WidthRequest="90" HeightRequest="40" Padding="5" >
<Frame BackgroundColor="Red" CornerRadius="8" WidthRequest="80" HeightRequest="30" Padding="0">
<Label Text="11111" TextColor="White" FontSize="20" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
</Frame>
</Frame>
</Frame>
</StackLayout>
You could put the above code in a custom view and binding the color and text in code behind.

Related

How to make Grid Row dynamic in CollectionView control of .Net MAUI?

I'm trying to achieve dynamic row height in CollectionView control, so that when ever I have more text for a particular property it will extend the height of the frame automatically.
I have tried with ListView as well using HasUnEvenRow property "true" but with that also it's not working.
Here is my code:
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Grid RowDefinitions="*,60" BackgroundColor="{StaticResource PageBackgroundColor}" Padding="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<CollectionView Grid.Row="0" HorizontalOptions="Fill" VerticalOptions="Fill"
ItemsSource="{Binding Inspections}"
VerticalScrollBarVisibility="Always"
ItemsLayout="VerticalList" ItemSizingStrategy="MeasureAllItems">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame Padding="15" HasShadow="False">
<Grid HorizontalOptions="Fill"
VerticalOptions="Fill"
BackgroundColor="White"
RowSpacing="25"
RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="*,Auto">
<StackLayout
Orientation="Horizontal"
Grid.Row="0"
Grid.Column="0">
<Label Text="{Binding Path=BusinessName}"
Style="{StaticResource LabelTitleStyle}" />
<Grid Padding="15,0,0,0">
<baseChip:Chip
HorizontalOptions="Fill" VerticalOptions="Fill"
Style="{StaticResource ChipContainer}"
HasShadow="False"
BackgroundColor="{Binding Path=ChipBackgroundColor}">
</baseChip:Chip>
<Label
Text="{Binding Path=ChipText}"
Style="{StaticResource ChipLabel}"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="{Binding Path=ChipTextColor}"/>
</Grid>
</StackLayout>
<Image
Grid.Row="0"
Grid.Column="1"
HorizontalOptions="End"
VerticalOptions="Center"
Aspect="AspectFit">
<Image.Source>
<FontImageSource Glyph="{x:Static helper:MaterialFontHelper.FilePdfBox}"
Color="{StaticResource DarkGray}"
Size="20"
FontFamily="MaterialDesignIcons"/>
</Image.Source>
</Image>
<Grid Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
RowDefinitions="Auto, Auto"
ColumnDefinitions="*, *">
<Label
Grid.Row="0"
Grid.Column="0"
Text="Inspection Type"
Style="{StaticResource LabelKeyStyle}" />
<Label
Grid.Row="1"
Grid.Column="0"
Text="ksd kahdkahd kahd kahd aojsoiud aasjlj sdlkja dlkja da asdadas alsajdlaksjdlajd alsjdalkjd alksjd sa"
Style="{StaticResource LabelValueStyle}" />
<Label
Grid.Row="0"
Grid.Column="1"
Text="Primary Inspector"
Style="{StaticResource LabelKeyStyle}" />
<Label
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource LabelValueStyle}" >
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding Path=InspectorFirstName}"/>
<Span Text=" "/>
<Span Text="{Binding Path=InspectorLastName}"/>
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>
<Grid Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
RowDefinitions="*, *"
ColumnDefinitions="*, *">
<Label
Grid.Row="0"
Grid.Column="0"
Text="Scheduled Date"
Style="{StaticResource LabelKeyStyle}" />
<Label
Grid.Row="1"
Grid.Column="0"
Text="{Binding Path=ScheduledStartDate, Converter={StaticResource dateFormatter},ConverterParameter='long'}"
Style="{StaticResource LabelValueStyle}" />
<Label
Grid.Row="0"
Grid.Column="1"
Text="Completed Date"
Style="{StaticResource LabelKeyStyle}" />
<Label
Grid.Row="1"
Grid.Column="1"
Text="{Binding Path=CompletionDate, Converter={StaticResource dateFormatter},ConverterParameter='long'}"
Style="{StaticResource LabelValueStyle}" />
</Grid>
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<StackLayout
Grid.Row="1"
VerticalOptions="End"
HorizontalOptions="FillAndExpand"
BackgroundColor="{StaticResource White}">
<controlTemplate:BeginInspectionContentView></controlTemplate:BeginInspectionContentView>
</StackLayout>
</Grid>
</StackLayout>
Output & expected output image attached here:
How to achieve dynamic height for this UI? Any help or suggestions are welcome.
There are three solutions to this problem:
You can change CollectionView to FlexLayout with BindableLayout.ItemsSource and BindableLayout.ItemTemplate. Then you don't need to set Height for item or FlexLayout because all are dynamic.
You can set a value to collectionView.HeightRequest using MVVM. When item increase, the height will increase as well. For more details about this solution, you may refer to the link.
You can use CollectionView.Behaviors to calculate the Height of every item to increase the height of ContentView. For more details about this solution, you may refer to the link.
You have Grid. With StackLayout. With Grid. With Small Grids inside it.
I will put aside for now, that this is extremely bad for rendering. (It is serious problem however...)
None of the expected output justify this organization.
If you need for element to occupy 2 columns, you can use columns span 2.
Before anything else, please combine all 4 grids in a single grid.
After you are done with that, limit your VisualElements height, or, even better, limit the content you are putting in them. (Because cut content looks bad. You should be using something like "show more" link, that opens popup or whatever).
If you have any questions, please ask.
Edit: Let me clarify:
You have the same Grid code. Most important part - columns are * , * , and rows are auto height. And every 2 rows, you create another GRID, that is copy of the first GRID.
This is extremely counterproductive.
Also, just because you can do something, and it is "working in xamarin", it doesn't mean that you should do it.
First, put everything in the same grid, instead of nesting it and copying grids, then you can start working on CLIP/Height limit/etc.
(Recommending you to test it on IOS, because once you get it running fine there, it is much easier to fine-tune it for other platforms, than the other way around.)

Why only the second Frame shows in a StackLayout

I have a Stacklayout that contains another Stacklayout and an Image. In order to have rounded corners for both children of the top Stacklayout, I added Frame for each child. Now, only the second child (Image) appear no matter how I change the visibility of the two children.
<StackLayout HeightRequest="135" WidthRequest="100" Margin="5,5" Grid.Row="0" Spacing="0" HorizontalOptions="Start" VerticalOptions="Start">
<Frame IsVisible="false" CornerRadius="5" IsClippedToBounds="True" Padding="0" HasShadow="False" x:Name="InitialsFrame">
<StackLayout IsVisible="false" HeightRequest="100" WidthRequest="100" BackgroundColor="{Binding Color}">
<Label FontSize="60" Text="{Binding Initials}" TextColor="White" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" MaxLines="1" x:Name="InitialsLabel">
</Label>
</StackLayout>
</Frame>
<Frame IsVisible="true" CornerRadius="5" IsClippedToBounds="True" Padding="0" x:Name="ImageFrame">
<Image IsVisible="true" Source="{Binding Image}" HeightRequest="100" WidthRequest="100" x:Name="IconImage" />
</Frame>
</StackLayout>
Without the two Frames, I could choose which child to appear by setting IsVisible to True. But now, setting IsVisible does not work any more. Is there another way to conditionally choose which child to appear?

CollectionView disabling scrolling in Xamarin forms

I am trying to disable scrolling in my collection view. The reason why I want to do that is there is a scroll view already in my XAML code. When I try to scroll all page elements on the page, collection view elements are also scrolled themselves but I don't want that.
<ScrollView>
<StackLayout Padding="20" Spacing="20" >
<Frame CornerRadius="15"
BackgroundColor="#A6EDB3"
VerticalOptions="StartAndExpand"
HeightRequest="200"
IsClippedToBounds="True"
Padding="0" >
<StackLayout Padding="10,5,10,5"
Orientation="Horizontal" >
<Image Source="settingsIcon"
HeightRequest="25"
WidthRequest="25"
Aspect="Fill" />
<Label Text="Filter"
FontSize="Medium"
VerticalTextAlignment="Center"
VerticalOptions="Center"/>
</StackLayout>
</Frame>
<Label Text="Vocabulary topics" TextColor="black" FontSize="20" FontAttributes="Bold" ></Label>
<CollectionView x:Name="topics" Scrolled="topics_Scrolled" VerticalScrollBarVisibility="Never" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0,10,0,10">
<Frame HasShadow="False"
HeightRequest="60"
CornerRadius="15"
BackgroundColor="{Binding BackgroundColor}"
HorizontalOptions="Fill" >
<StackLayout Orientation="Horizontal">
<Frame BackgroundColor="{Binding BoxColor}" WidthRequest="40" ></Frame>
<StackLayout>
<Label Text="{Binding Name}"></Label>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ScrollView>
Having two scrolls on the same page is not the correct way.
Also if you just want to place items above/below your collectionView use the Header/Footer properties then!!
For instance, for the current design, your CollectionView could look something like below and work as you want it to.
<CollectionView Padding="20" x:Name="topics" Scrolled="topics_Scrolled" VerticalScrollBarVisibility="Never" >
<CollectionView.Header>
<StackLayout Spacing="20" >
<Frame CornerRadius="15"
BackgroundColor="#A6EDB3"
VerticalOptions="StartAndExpand"
HeightRequest="200"
IsClippedToBounds="True"
Padding="0" >
<StackLayout Padding="10,5,10,5"
Orientation="Horizontal" >
<Image Source="settingsIcon"
HeightRequest="25"
WidthRequest="25"
Aspect="Fill" />
<Label Text="Filter"
FontSize="Medium"
VerticalTextAlignment="Center"
VerticalOptions="Center"/>
</StackLayout>
</Frame>
<Label Text="Vocabulary topics" TextColor="black" FontSize="20" FontAttributes="Bold" ></Label>
</StackLayout>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0,10,0,10">
<Frame HasShadow="False"
HeightRequest="60"
CornerRadius="15"
BackgroundColor="{Binding BackgroundColor}"
HorizontalOptions="Fill" >
<StackLayout Orientation="Horizontal">
<Frame BackgroundColor="{Binding BoxColor}" WidthRequest="40" ></Frame>
<StackLayout>
<Label Text="{Binding Name}"></Label>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Note: you might have to adjust the margin and padding properties for it to look the exact same. My code is just an example.
For more information on CollectionView check: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/
You can use InputTransparent.
In your case what I would do would be to wrap the CollectionView in a <ContentView> as:
<ContentView InputTransparent="True"
x:Name="content">
<CollectionView ItemsSource="{Binding Items}"...>
...
</CollectionView>
</ContentView>
Create a scroll event to your scroll view:
<ScrollView Scrolled="ScrollView_Scrolled">
...
</ScrollView>
Then, with this event, make sure that the InputTransparent switches depending on the scroll position:
private void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
{
var scrollView = sender as ScrollView;
// Get the height of your scroll view
var contentSize = scrollView.ContentSize.Height;
// Get the max position of the scrollview
var maxPos = contentSize - scrollView.Height;
// Compare it to the current position
if (Convert.ToInt16(e.ScrollY) >= Convert.ToInt16(maxPos))
{
// Switch input transparent value
content.InputTransparent = false;
}
else if (Convert.ToInt16(e.ScrollY) == 0)
{
content.InputTransparent = true;
}
}
This is perfectly fine to use two scrollable controls on the same page for what you want to do. And I don't think <CollectionView.Header> would give you the result you want.
I hope it was helpful! 🙂

How to add an Image side to the back button in navigation bar

I am working on xamarin. form application, where I need to add a image side to the
back button in the navigation bar.
I have tried
1. <NavigationPage.TitleView><image/></NavigationPage.TitleView> -here the view is away from the back button. the space between back button and image is more.
2. created a custom navigation as following.
<Grid BackgroundColor="{Binding ActionbarColor}">
<StackLayout Orientation="Horizontal" Spacing="0" HeightRequest="60" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Image Source="back.png" HeightRequest="20" WidthRequest="20" HorizontalOptions="Start" VerticalOptions="CenterAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding MoveBackCommand}"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<Imge Source="defaultUser.png">
<Label />
<Label x:Name="TitleLabel" VerticalTextAlignment="Center" VerticalOptions="CenterAndExpand" TextColor="White" FontAttributes="Bold" FontSize="Medium" HorizontalOptions="FillAndExpand"/>
</StackLayout>
</Grid>
with 2nd, the view is blinking while navigating to the back view.
Apart of these two are there any ways to display the image in the navigation bar side the back button
Use Grid to Change
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<BoxView BackgroundColor="Aqua" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="5"/>
<Button Image="BackIcon" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="1" Command="GoBack"/>
<Image Source="Logo" Aspect="AspectFit" BackgroundColor="Transparent" Grid.Column="2" Grid.Row="0" Grid.ColumnSpan="1"/>
</Grid>
It not works follow this - if it works let me know

ContentPage containing list view display very slow on Android

I am not sure whether this is my problem or a problem with Xamarin.
On an iPad my application can load the page which contain a list of data in ListView.
The View Cell inside the ListView can be slightly complex. It contains a profile image, some smaller icons using, which are using FFImageLoading.
On iPad, when I tap a cell of the first page to go into the next page, which contains a list of data, it loads very smoothly.
On Android, when I tap on a cell to go into the next page, the detection of the tap is slow and the loading time of the ListView also very slow.
Is there anyway enhance the performance? I thought it was the data I loaded from SQLite. However, after I comment out the ItemsSource on ListView, the loading time is fine.
This is the datatemplate I've used for displaying the cell.
<ViewCell>
<ViewCell.View>
<StackLayout Spacing="0" Padding="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" Spacing="10" Padding="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<controls:CircleImage
Style="{StaticResource profileImageStyle}"
Margin="10, 10, 10, 10"
Source="{Binding Source}"
BorderColor="White"
BorderThickness="2"
VerticalOptions="Center"
HorizontalOptions="Center">
<controls:CircleImage.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChildDetailTapped" />
</controls:CircleImage.GestureRecognizers>
</controls:CircleImage>
<StackLayout VerticalOptions="Fill" Spacing="1" Padding="0,20,0,10" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChildDetailTapped" />
</StackLayout.GestureRecognizers>
<StackLayout
Orientation="Vertical"
VerticalOptions="Center"
HorizontalOptions="StartAndExpand">
<Label x:Name="childName" Text="{Binding DisplayName}" Style="{StaticResource normalFont}"> </Label>
<local:ChildInfoIconsView
Child="{Binding .}"
VerticalOptions="Fill">
</local:ChildInfoIconsView>
<Label
x:Name="childNotes"
Style="{StaticResource footnoteFont}"
Text="{Binding ChildNotes, StringFormat={x:Static local:AppResources.formatNotes}}"
IsVisible="{Binding HasChildNotes}">
</Label>
<Label
x:Name="noPickupReason"
Style="{StaticResource footnoteFont}"
Text="{Binding NoPickupReason, StringFormat={x:Static local:AppResources.formatNoPickupReason}}"
IsVisible="{Binding HasNoPickupReason}">
</Label>
<Label
x:Name="absentReason"
Style="{StaticResource footnoteFont}"
Text="{Binding AbsentReason, StringFormat={x:Static local:AppResources.formatAbsentReason}}"
IsVisible="{Binding HasAbsentReason}">
</Label>
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="FillAndExpand" HorizontalOptions="End">
<StackLayout.Padding>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone>5, 20, 10, 20</OnIdiom.Phone>
<OnIdiom.Tablet>10, 30, 30, 30</OnIdiom.Tablet>
</OnIdiom>
</StackLayout.Padding>
<Image
Style="{StaticResource listviewButtonStyle}"
IsVisible="{Binding EnabledSigning, Source={x:Reference page}}"
Source="ic_action_yes.png"
VerticalOptions="FillAndExpand"
HorizontalOptions="End">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnAttend" />
</Image.GestureRecognizers>
<Image.Margin>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone>0, 0, 5, 0</OnIdiom.Phone>
<OnIdiom.Tablet>5, 5, 20, 5</OnIdiom.Tablet>
</OnIdiom>
</Image.Margin>
</Image>
<Image
Style="{StaticResource listviewButtonStyle}"
IsVisible="{Binding EnabledSigning, Source={x:Reference page}}"
Source="ic_action_no.png"
VerticalOptions="FillAndExpand"
HorizontalOptions="End">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnAbsent" />
</Image.GestureRecognizers>
<Image.Margin>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone>5, 0, 0, 0</OnIdiom.Phone>
<OnIdiom.Tablet>20, 5, 5, 5</OnIdiom.Tablet>
</OnIdiom>
</Image.Margin>
</Image>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
Try setting a listview caching strategy to improve performance.
See here for more details
Be aware though that you may need to jump through some hoops to use RecycleElement with FFImageLoading. The issue is described here
Also, please post your data template so we can see if it can be simplified.
Use RecyclerView
https://blog.xamarin.com/recyclerview-highly-optimized-collections-for-android-apps/
OR
Use FFImageLoading
https://github.com/luberda-molinet/FFImageLoading
to optimize and for smoother scroll

Resources