Xamarin Forms CollectionView with square images - xamarin

I want to display list of images as a grid with several rows (2 or 4) with Xamarin Forms. Each cell of the grid must be square. I'm using CollectionView with vertical layout, required span and fixed HeightRequest in DataTemplate. I get multicolumn grid, but I cannot make images (cells) to be squared.
<CollectionView ItemsSource="{Binding .}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Image
HeightRequest="100"
x:Name="imageCell"
Aspect="AspectFill"
Source="{Binding .}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

Thanks to other answers, I've ended up with custom ContentView setting up HeightRequest equal to Width and the image inside it.
public class SquareView : ContentView
{
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
HeightRequest = Width;
}
}
and XAML
<CollectionView
ItemsSource="{Binding .}">
<CollectionView.ItemsLayout>
<GridItemsLayout
Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<views:SquareView>
<Image
HeightRequest="20"
Aspect="AspectFill"
Source="{Binding .}" />
</views:SquareView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

I've reproduced a small sample.
Only thing I added was to wrap the image inside a contentview, to apply a padding (which you can ignore)
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView Padding="0">
<Image Source="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSzJUY4JZTX9oYsSl1jclFvdsoXhtA0AfxqKkYX2P81Qb3cyX9o"
HeightRequest="150"
Aspect="AspectFill"
/>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

Don't use <Image></Image> as your root layout in DataTemplate.
Instead, use one of the Layout controls (StackLayout, AbsoluteLayout etc.) or ContentView and then apply some styles or whatever you prefer to this root layout of your CollectionView.
<DataTemplate>
<StackLayout>
<Image
HeightRequest="100"
x:Name="imageCell"
Aspect="AspectFill"
Source="{Binding .}" />
</StackLayout>
</DataTemplate>

Related

Xamarin Forms CollectionView Last item not showing properly

I have a simple horizontal CollectionView which works fine except that the last item is not showing properly and i have to force scroll it to see it.
I tried all kinds of padding and margins but the only property that will make it show is if make ItemSpacing="20" or bigger. The problem is that, if i added more items, the issue still will show again and this is a dynamic list.
this is the code for the stacklayout under the content page directly.
<StackLayout VerticalOptions="Center" HorizontalOptions="FillAndExpand">
<StackLayout BackgroundColor="LightGray" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" >
<CollectionView HeightRequest="40" VerticalOptions="Fill" HorizontalOptions="Fill" Margin="5" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" ItemSpacing="10"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Open (Item)</x:String>
<x:String>Side Menu</x:String>
<x:String>How to?</x:String>
<x:String>Back/Cancel</x:String>
<x:String>Logout</x:String>
<x:String>Yes/No</x:String>
<x:String>App Info</x:String>
</x:Array>
</CollectionView.ItemsSource>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame BackgroundColor="White" BorderColor="DarkCyan" CornerRadius="20" HasShadow="False" Padding="10" Margin="0">
<Label Text="{Binding}" TextColor="DarkCyan"/>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</StackLayout>

xamarin collectionview cell custom border

How to add custom border to the collectionview cell like the image?
<CollectionView HeightRequest="100" ItemsSource="{Binding Test}">
<CollectionView.ItemsLayout >
<GridItemsLayout Orientation="Vertical" Span="4"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Image Source="{Binding Image}" ></Image>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Put the collection view into a colored container could do that, and set margin for the collection view as the width of border, like
<StackLayout BackgroundColor="Black" HorizontalOptions="FillAndExpand">
<CollectionView Margin="4,0">
</CollectionView>
</StackLayout>
I use a sub-class frame to add border, please take a look at the following code:
Creating custom frame in .Net standard library project(shared code).
public class MyFrame : Xamarin.Forms.Frame
{
}
Custom renderer in Android platform.
[assembly: ExportRenderer(typeof(MyFrame), typeof(MyFrameRenderer))]
namespace demo3.Droid
{
[Obsolete]
class MyFrameRenderer : VisualElementRenderer<Frame>
{
public MyFrameRenderer()
{
SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.blue_rect));
}
}
}
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand">
<control:MyFrame Padding="5">
<CollectionView BackgroundColor="Transparent" ItemsSource="{Binding Test}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="4" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame BackgroundColor="Transparent" BorderColor="Gray">
<Label Text="{Binding .}" TextColor="Black" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</control:MyFrame>
</StackLayout>
</ContentPage.Content>
The screenshot:

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 make Scrollable Label in List Xamarin Forms

I have a List view with fixed Row Height. Inside List view there is Label with large description. I am trying to make this label scroll able. Can anyone help please.
<ListView ItemsSource="{Binding ServiceList, Mode=TwoWay}" RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:ServiceModel">
<ViewCell>
<ScrollView
Margin="0"
Padding="0"
HeightRequest="50">
<Label
LineBreakMode="WordWrap"
Style="{StaticResource blackColorLabel}"
Text="{Binding ServiceDescription}" />
</ScrollView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
As mentioned in the comments, you shouldn't nest scrollviews because the behavior is erratic. You can use BindableLayout though.
<StackLayout BindableLayout.ItemsSource="{Binding ServiceList}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="models:ServiceModel">
<ScrollView
Margin="0"
Padding="0"
HeightRequest="50">
<Label
LineBreakMode="WordWrap"
Style="{StaticResource blackColorLabel}"
Text="{Binding ServiceDescription}" />
</ScrollView>
</DataTemplate>
</BindableLayout.ItemTemplate>
RowHeight is not a thing for BindableLayouts, but you could probably wrap your Label in a ContentView and set the HeightReqeust of the ContentView to 100. Or maybe even just set your Label Height Request to 100 and see what that does.

Xamarin Listview Itemtemplate Appearance issue

Im working on styling up my ListView. In my ItemTemplate are only two Labels in a Stacklayout with Vertical Orientation. As you can see in the Picture there are some spacing between the two Labels. I dont want this space and have the Labels direct under each other. Has someone an advice?
ListView with Label-Background
<ListView x:Name="ItemList"
ItemSelected="ItemList_ItemSelected"
HasUnevenRows="True"
IsGroupingEnabled="true"
GroupDisplayBinding="{Binding LongName}"
GroupShortNameBinding="{Binding ShortName}"
SeparatorVisibility="None">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal"
BackgroundColor="{StaticResource LightGreyColor}"
Padding="20,10,0,10">
<Label Text="{Binding LongName}"
Style="{DynamicResource DateLabel}"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Vertical"
Padding="20,10,0,10">
<Label Text="{Binding Name}"
Style="{DynamicResource ItemTitleLabel}"
BackgroundColor="Bisque"/>
<Label Text="{Binding Description}"
Style="{DynamicResource SubTitleLabel}"
BackgroundColor="Bisque"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I remember that default spacing between items in StackLayout (and between rows in a Grid) is more than 0. If you want no space between items you can simply add Spacing="0" attribute in your StackLayout.
StackLayout has a default Spacing of 10, you need to set it to 0 if you don't want the spacing between items.

Resources