Horizontal scrollview in XAML - xaml

I have a list of MyClass:
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
I would like to display this list in a Horizontal scrollview. Im doing it in XAML and having a hard time finding examples. I got this now:
<ScrollView Orientation="Horizontal">
<ListView x:Name="MyClassList"
ItemsSource="{Binding MyClassList}"
RowHeight="210">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name:" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="1" Grid.Column="0" Text="Type:" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding Type}" LineBreakMode="TailTruncation"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollView>
But this presents the list in a vertical manner. Any tips on how to scroll this list horizontally?

Not entirely sure if I understand the issue. My interpretation is that you want the ListView itself to add elements horizontally instead of vertically.
This can be achieved by settings the ListViews ItemsPanel to a horizontal StackPanel.
Try this:
<ScrollView Orientation="Horizontal">
<ListView x:Name="MyClassList"
ItemsSource="{Binding MyClassList}"
RowHeight="210">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name:" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="1" Grid.Column="0" Text="Type:" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding Type}" LineBreakMode="TailTruncation"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollView>

This has been solved with a custom class called ItemsView (not to be confused with Xamarin's ItemsView for data templating in a ListView) which implements a ScrollView/StackPanel pattern. Please see the code: https://gist.github.com/fonix232/b88412a976f67315f915

Related

Xamarin Forms Access parent collection item

I have a problem. I have a CollectionView with a List<Album> albumList with inside an Album a List<Picture> Pictures. Now I created the following XAML:
<CollectionView ItemsSource="{Binding albumList}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="90"/>
<RowDefinition Height="5"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="30"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Padding="0" Margin="0"
BindableLayout.ItemsSource="{Binding Pictures}" Orientation="Vertical">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Grid.Column="1" Text="{Binding albumList.Price, StringFormat='€ {0:F2}'}" TextColor="Black" FontSize="16" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Now I want to access the current Album.Price inside the following line: <Label Grid.Column="1" Text="{Binding albumList.Price, StringFormat='€ {0:F2}'}" TextColor="Black" FontSize="16" />
This is outside the current binding of Pictures, so how can I enter a value from outside the current Binding?
You can define the BindingContext from a reference of an element outside the nested Collection, give a name to your StackLayout for example:
<StackLayout x:Name="ElementForReference" Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Padding="0" Margin="0"
BindableLayout.ItemsSource="{Binding Pictures}" Orientation="Vertical">
And then define the BindingContext with the reference of the element outside the nested one:
{Binding BindingContext.Price, Source={x:Reference Name=ElementForReference}}

Nested List view in Xamarin forms xaml with MVVM

I want nested list view like below in xamarin forms
I am using XAML and wants to bind using MVVM
My model as below
public class LineItemTaxDto
{
public int InvoiceLineItemId { get; set; }
public int InvoiceId { get; set; }
public int TaxId { get; set; }
public decimal TaxRate { get; set; }
public decimal TaxAmount { get; set; }
public string TaxName { get; set; }
public ObservableCollection<LineItemTaxDto> SubTaxes { get; set; }
}
In this Group tax is my main taxname and it contains sub taxes (VAT, GST).
EDIT
My xaml code is below
<ListView x:Name="TaxListView" ItemsSource="{Binding Invoice.Taxgroup}" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="false" RowHeight="35">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Spacing="0" HorizontalOptions="End">
<Label Text="{Binding Source={x:Reference TaxListView}, Path=BindingContext.CurrentOutlet.ReceiptTemplate.TaxLable, StringFormat='{0} ('}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
<Label Text="{Binding TaxName, StringFormat='{0})'}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" HorizontalTextAlignment="End" Margin="0,5,0,5" />
</StackLayout>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding TaxAmount, Converter={Helpers:CurrencyAmountConverter}}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
<ListView x:Name="TaxListView2" ItemsSource="{Binding Invoice.Taxgroup}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="false" RowHeight="35">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Spacing="0" HorizontalOptions="End">
<Label Text="{Binding TaxName}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" HorizontalTextAlignment="End" Margin="0,5,0,5" />
</StackLayout>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding TaxAmount, Converter={Helpers:CurrencyAmountConverter}}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
But is not working. please help me to what I am going wrong. Thanks in advance
Solved
I solve above result using single list no need to nested list view.
My code as below.
My xaml code look like:
<ListView
x:Name="TaxListView"
ItemsSource="{Binding Invoice.ReceiptTaxList}"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
SeparatorVisibility="None"
HasUnevenRows="false"
RowHeight="35"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding TaxName}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" HorizontalTextAlignment="End" Margin="0,5,0,5" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding TaxAmount, Converter={Helpers:CurrencyAmountConverter}}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My view model code look like:
var TMPTaxList = invoice.InvoiceLineItems.Where(x => x.TaxId > 1).Select(x =>
{
return new KeyValuePair<LineItemTaxDto, ObservableCollection<LineItemTaxDto>>(new LineItemTaxDto()
{
InvoiceLineItemId = x.Id,
InvoiceId = x.InvoiceId,
TaxId = x.TaxId,
TaxRate = x.TaxRate,
TaxAmount = x.TaxAmount,
TaxName = "Tax (" + x.TaxName + ")"
}, x.LineItemTaxes);
});
var taxes = new ObservableCollection<LineItemTaxDto>();
foreach (var tax in TMPTaxList.GroupBy(tax => tax.Key.TaxId)
.Select(grp => grp.First()))
{
taxes.Add(tax.Key);
foreach (var subtax in tax.Value.Where(x => x.TaxId > 0))
{
taxes.Add(subtax);
}
}
invoice.ReceiptTaxList = taxes;

How to concatenate strings in Xamarin.Forms?

I want my two strings to be diplayed on just a single line. Is it possible for it to appear like this:
Curry Stephen
using this code
Text="{Binding EMP_LAST_NAME + EMP_FIRST_NAME}" ? ? ?
I currently have this code. Thanks a lot.
<ListView ItemsSource="{Binding EmployeesList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding EMP_LAST_NAME}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding EMP_FIRST_NAME}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
You can't bind to multiple properties on a View Element.
In this case you should create a new property which does the format you want and bind it to the View.
Example:
public class EmployeeViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
Then in XAML:
<Label Text="{Binding FullName}"/>
Another approach:
As suggested in the comments we can also use FormattedText property in a Label:
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding FirstName}" />
<Span Text="{Binding LastName}"/>
</FormattedString>
</Label.FormattedText>
Use FormattedText property in xamarin forms Label as follow:
<Label Grid.Column="1" Grid.Row="1">
<Label.FormattedText>
<FormattedString>
<Span TextColor="White" FontSize="18" Text="{Binding EMP_LAST_NAME'}"/>
<Span TextColor="White" FontSize="18" Text="{Binding EMP_FIRST_NAME}"/>
</FormattedString>
</Label.FormattedText>
</Label>
Xamarin.Forms Label
And also you can add Style to avoid code duplication for TextColor, FontSize and other properties in your code.
Styling Xamarin.Forms Apps using XAML Styles
<Label Text="{Binding Value, StringFormat='string before value {0:F0} string after value'}"/>
Suppose your value is 1234.
The output will be in this case:
string before value 1234 string after value
You could use IValueConverter, which will accept Employee and will return full name.
Or you could use MultiComponentLabel. It allows you to bind couple different values to one Label.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="Page"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:SuperForms.Controls;assembly=SuperForms.Controls"
x:Class="SuperForms.Samples.MultiComponentLabelPage">
<controls:MultiComponentLabel Margin="0,20,0,0">
<controls:MultiComponentLabel.Components>
<controls:TextComponent Text="{Binding EMP_LAST_NAME}"/>
<controls:TextComponent Text="{Binding EMP_FIRST_NAME}"/>
</controls:MultiComponentLabel.Components>
</controls:MultiComponentLabel>
</ContentPage>
Just use MultiComponentLabel instead of couple Labels
For your ListView
<ListView ItemsSource="{Binding EmployeesList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2" />
<controls:MultiComponentLabel Grid.Row="1" Grid.Column="1">
<controls:MultiComponentLabel.Components>
<controls:TextComponent Text="{Binding EMP_LAST_NAME}"/>
<controls:TextComponent Text="{Binding EMP_FIRST_NAME}"/>
</controls:MultiComponentLabel.Components>
</controls:MultiComponentLabel>
</Grid>
</ViewCell>
Xamarin Forms 4.7 add support for Multibinding which can be used like this:
<Label>
<Label.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</Label.Text>
</Label>
In the example provided, "FirstName" and "LastName" are regular viewmodel properties defined in the class:
public string FirstName { get; set; }
public string LastName { get; set; }

Xamarin.Forms Center ListView Items

How do I center listview items within a Xamarin.Forms app?
The listview items are currently left-aligned.
I want them to be center-aligned.
I have the following code:
<ListView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Results}" HorizontalOptions="Center">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding FirstName}" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding LastName}" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Scott the Grid always expands to occupy 100% of the available space. There is no way as far as I can tell to override this even explicitly setting a widthrequest on the grid doesn't even seem to help.
I assume you have a larger layout requirement your attempting to satisfy as centering first and Last name in a listView would be better accomplished with a Stacklayout instead of a grid.
IE
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding FirstName}" HorizontalOptions="EndAndExpand"/>
<Label Text="{Binding LastName}" HorizontalOptions="StartAndExpand"/>
</StackLayout>
</ViewCell.View>
But assuming you really need a Grid then I can offer only 1 addition to Tomasz's suggestion.
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding FirstName}" HorizontalOptions="End"/>
<Label Grid.Row="0" Grid.Column="2" Text="{Binding LastName}" />
</Grid>
This create two Columns on the outside to eat up the space forcing the names to the middle.
Without having actually tried it...
Your cell view is <Grid HorizontalOptions="Center">, but the Grid itself is expanded to fit the cell, so the HorizontalOptions there is inconsequential.
You need to put a secondary container inside that Grid that will be centered,
something like:
<ViewCell.View>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalOptions="Center">
<Label Grid.Row="0" Grid.Column="0" Text="{Binding FirstName}" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding LastName}" />
</StackPanel>
</Grid>
</ViewCell.View>
You could also try (if the above doesn't work) adding 3 columns (and 3 rows for vertical alignment) to the Grid with sizes (auto)-(*)-(auto) and putting the StackPanel in the center cell.
Maybe this way:
<Label Grid.Row="0" Grid.Column="0" Text="{Binding FirstName}" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding LastName}" HorizontalOptions="Center"/>
EDITED:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding FirstName}" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding LastName}" HorizontalOptions="Center"/>
Just by adding HorizontalOptions="Center" to Label worked for me, good luck
<ListView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Results}" HorizontalOptions="Center">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Label HorizontalOptions="Center" Grid.Row="0" Grid.Column="0" Text="{Binding FirstName}" />
<Label HorizontalOptions="Center" Grid.Row="0" Grid.Column="1" Text="{Binding LastName}" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Xamarin datatemplate with many rows

I have a listview and a datatemplate.
<ListView x:Name="list"
ItemsSource="{Binding MyList}"
HorizontalOptions="Center"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="First"></Label>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding First}"></Label>
<Label Grid.Row="1" Grid.Column="0" Text="Second"></Label>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding Second}"></Label>
<Label Grid.Row="2" Grid.Column="0" Text="Third"></Label>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding Third}"></Label>
<Label Grid.Row="3" Grid.Column="0" Text="Fourth"></Label>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding Forth}"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The problem here is that only the first two rows are displayed. Is there maybe some limitation to a viewCell that only lets it show two rows?
Is there maybe a better way to achieve what I am trying to do? Can I maybe use a table inside the datatemplate? Thank you
Try Adding to ListView a RowHeight definition.
For example, I would try something like this:
<ListView x:Name="list"
ItemsSource="{Binding MyList}"
HorizontalOptions="Center"
VerticalOptions="FillAndExpand"
RowHeight="100">
...
</ListView>