First of all im just starting with xamarin forms, and c# i have managed to use listviews, however im a lil bit confused when it comes to grids, basically what i need is to bind an object to a serie of grids that behave like this:
2 columns per row, i have managed to do it with this code:
<Grid HorizontalOptions="Fill" VerticalOptions="Fill" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!--fila 1-->
<StackLayout Grid.Column="0" Grid.Row="0" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="0" BackgroundColor="Red" HorizontalOptions="Fill"></StackLayout>
<!--fila 2-->
<StackLayout Grid.Column="0" Grid.Row="1" BackgroundColor="Red" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="1" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<!--fila 3-->
<StackLayout Grid.Column="0" Grid.Row="2" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="2" BackgroundColor="Red"
HorizontalOptions="Fill"></StackLayout>
</Grid>
However, i dont know how to fill the grids with information dynamically. with the listview is quite simple since its only with the bind command, here i dont have idea, can someone please point me into the right direction?
thanks.
int row = 0;
int col = 0;
// data is a List<string>
foreach (var text in data) {
var label = new Label() { Text = text };
grid.Children.Add(box, col, row);
col++;
if (col > 1) {
col = 0;
row++;
}
}
Related
I have a dynamic checkbox,
private async void GetOffices()
{
string url = $"{baseurl}/get-offices";
var response = await httpservices.SendGetRequest(url);
for (int i = 0; i < response.Count; i++)
{
Offices.Add(new Office() { id = (string)response[i]["id"], OfficeName = (string)response[i]["office_name"] });
}
}
And my XAML looks like this:
<CollectionView ItemsSource="{Binding Offices}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:Office" >
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame Grid.ColumnSpan="2" Padding="15" BackgroundColor="LightBlue" x:Name="OfficeSelection" CornerRadius="0">
<Grid Grid.ColumnSpan="2" RowDefinitions="Auto,Auto" ColumnDefinitions="40,*">
<CheckBox Grid.Column="0" x:Name="{Binding OfficeName}" CheckedChanged="OnCheckBoxCheckedChanged"/>
<Label TextTransform="Uppercase" Padding="10" FontSize="Default" Text="{Binding OfficeName}" Grid.Column="1"></Label>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And it will display like this:
What I want to do is, when I click the continue button, I can get all the checked checkboxes Name or value where in this case are: ADMISSION, CASHIER, PLENARY HALL and store it in array?
This is my button XAML:
<Button
Grid.Row="1"
Grid.Column="0"
Margin="5"
BorderWidth="0"
Command="{Binding GetSelectedOfficesCommand}"
CornerRadius="0"
Text="Continue" />
[RelayCommand]
public void GetSelectedOffices()
{
//CODe HERE
}
MAUI related MS Docs is quiet confusing for me. Anyone can give me a code snippet to solve this?
You should add a boolean property to the office view model and bind it to the checkbox IsChecked. Then in your GetSelectedOffices function you just filter by this property:
var selectedNames = Offices.Where(x => x.IsSelected).Select(x => x.OfficeName);
Hope everyone is well. I'm running into a UI problem with my cross platform app for xamarin.forms. Basically it shows different on ios when compared to android.
I have a function on the AdminPage that when button clicks opens the PopupView, the popUp will either contain details on the user, or the order, for this example we are using orderDetails
private void ShowUserDetails_Clicked(object Sender, EventArgs e)
{
Button button = (Button)Sender;
string UserId = button.CommandParameter.ToString();
PopupNavigation.Instance.PushAsync(new PopupView(-1, int.Parse(UserId)));
}
The problem lies in the UI of the popUp, if you notice the android phone on the left of the image below. It loads the way I want it to. Displaying lable for email | phone number, followed by listview of OrderDetails below. As you can see it looks fine on the android. The problem is on the ios. Where is the whitespace coming from at the top of the PopUpView?
If you take a close look at the PopUpView on the iphone 11, there is a scroll bar to the right of the popUp . It starts where the text starts, and I cant scroll any further up. Its almost as if there is an extra row placed before anything else. I have looked extensively through the code and can't figure it out. So I want to remove the whitespace, have even tried listview.scrollTo for the listview but with no success..any help would be great thank you
<?xml version="1.0" encoding="utf-8" ?>
<pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BottleShop.Views.PopupView"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:converters="clr-namespace:BottleShop.ViewModels.Converters">
<StackLayout x:Name="StackSearchResultsOuter" BackgroundColor="Transparent" HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout.Resources>
<converters:ProductIdToProductNameConverter x:Key="converter"/>
</StackLayout.Resources>
<BoxView x:Name="InvisibleSpacer" BackgroundColor = "Transparent"
HeightRequest="80" />
<StackLayout x:Name="StackLayoutOrders" IsVisible="{Binding SLOrders}">
<ListView BackgroundColor="White" ItemsSource="{Binding PreviousOrderDetailsForUserLV}" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="20*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="0" x:Name="UserInfoLabel" HorizontalOptions="Center" Margin="10,10,0,0"/>
<BoxView Grid.Row="1"
Grid.ColumnSpan="4"
BackgroundColor="LightGray"/>
<Label Grid.Column="0" Text="Product" Grid.Row="1" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="1" Grid.Row="1" Text="Quantity" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="2" Grid.Row="1" Text="Price" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="3" Grid.Row="1" Text="Sub" HorizontalOptions="Center" Margin="0,10,0,0"/>
<!--<BoxView Grid.Row="1"
Grid.ColumnSpan="4"
HeightRequest="1"
BackgroundColor="LightGray"/> -->
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="43*" />
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="24*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" Text="{Binding ProductId, Converter={StaticResource converter}}"/>
<Label Grid.Column="1" Grid.Row="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Text="{Binding Quantity}"/>
<Label Grid.Column="2" Grid.Row="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Text="{Binding PriceOfItem, StringFormat='£{0:0.00}'}"/>
<Label Grid.Column="3" Grid.Row="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Text="{Binding SubtotalForThisItem, StringFormat='£{0:0.00}'}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout x:Name="StackLayoutUsers" IsVisible="{Binding SLUsers}">
<ListView x:Name="UsersListView" BackgroundColor="White" ItemsSource="{Binding AllExistingUsersLV}" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="20*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="Address Line 1" Grid.Row="0" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="1" Grid.Row="0" Text="Line 2" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="2" Grid.Row="0" Text="Town" HorizontalOptions="Center" Margin="0,10,0,0"/>
<Label Grid.Column="3" Grid.Row="0" Text="Mobile" HorizontalOptions="Center" Margin="0,10,0,0"/>
<BoxView Grid.Row="1"
Grid.ColumnSpan="4"
HeightRequest="1"
BackgroundColor="LightGray"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="43*" />
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="24*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" HorizontalOptions="StartAndExpand" VerticalOptions="EndAndExpand" Text="{Binding AddressLine1}"/>
<Label Grid.Column="1" Grid.Row="0" HorizontalOptions="StartAndExpand" VerticalOptions="EndAndExpand" Text="{Binding AddressLine2}"/>
<Label Grid.Column="2" Grid.Row="0" HorizontalOptions="EndAndExpand" VerticalOptions="EndAndExpand" Text="{Binding City}"/>
<Label Grid.Column="3" Grid.Row="0" HorizontalOptions="EndAndExpand" VerticalOptions="EndAndExpand" Text="{Binding MobileNumber}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
<!-- </Grid> -->
</pages:PopupPage>
using BottleShop.Data;
using BottleShop.Models;
using Microsoft.AppCenter.Crashes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Xamarin.Forms.Xaml;
namespace BottleShop.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PopupView //: ContentView
{
public ObservableCollection<OrderDetailsModel> PreviousOrderDetailsForUserLV { get; set; }
OrdersDatabaseController RecentOrdersController = new OrdersDatabaseController();
public ObservableCollection<UserModel> AllExistingUsersLV { get; set; }
UserDatabaseController UserController = new UserDatabaseController();
public bool SLOrders { get; set; }
public bool SLUsers { get; set; }
public PopupView(int OrderId, int UserId)
{
try
{
InitializeComponent();
//Show Orders PopUp
if (OrderId != -1)
{
SLOrders = true;
SLUsers = false;
List<OrderDetailsModel> RecentOrderDetailsList = RecentOrdersController.GetAllOrderDetailsByOrderId(OrderId);
PreviousOrderDetailsForUserLV = new ObservableCollection<OrderDetailsModel>(RecentOrderDetailsList as List<OrderDetailsModel>);
int startingHeightOfSL = 180;
int countOfOrderDetailRecords = PreviousOrderDetailsForUserLV.Count;
var tempHeightofStackSearchResults = countOfOrderDetailRecords * 40;
if (countOfOrderDetailRecords > 1)
{
StackSearchResultsOuter.HeightRequest = startingHeightOfSL + tempHeightofStackSearchResults;
}
else
{
StackSearchResultsOuter.HeightRequest = startingHeightOfSL;
}
UserInfoLabel.Text = "Email: " + PreviousOrderDetailsForUserLV[0].UserName + " | " + "Mobile: " + PreviousOrderDetailsForUserLV[0].UserMobileNumber;
}
else //Show Users PopUp
{
if(UserId != -1)
{
var a = 1;
SLOrders = false;
SLUsers = true;
List<UserModel> UsersList = UserController.GetUserWithId(UserId);
if (UsersList != null)
{
AllExistingUsersLV = new ObservableCollection<UserModel>(UsersList as List<UserModel>);
}
StackSearchResultsOuter.HeightRequest = 180;
}
}
BindingContext = this;
}
catch (Exception ex)
{
Crashes.TrackError(ex);
}
}
}
}
I am quite a beginner so take it easy on me. I have a collection view which is bound to an observable collection. The observable collection receives data and has the items but the Collectionview doesn't display anything at all. Could someone please help me with this. Thanks.
XAML
<CollectionView Grid.Row="1" ItemsSource="{Binding Fav}" x:Name="CVWatchItems" SelectionMode="Single" SelectionChanged="CVWatchItems_SelectionChangedAsync">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="8, 8, 8, 0">
<Frame BorderColor="LightGray" CornerRadius="0" HasShadow="True" Padding="5">
<Grid Padding="0" ColumnSpacing="0" RowSpacing="0" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ffimageloading:CachedImage Source="{Binding SingleimageUrl}" Aspect="AspectFill" WidthRequest="120" HeightRequest="100" Grid.Row="0" Grid.Column="0"/>
<Grid Grid.Row="0" Grid.Column="1" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Padding="0, 0, 0, 3" Text="{Binding Title}" LineBreakMode="TailTruncation" TextColor="Black" FontSize="Medium" Grid.Row="0"/>
<Label Text="{Binding Price, StringFormat='Nu.{0}'}" FontSize="Small" TextColor="Black" Grid.Row="1" HorizontalOptions="StartAndExpand" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding location}" FontSize="Small" TextColor="Gray" Grid.Column="0" HorizontalOptions="StartAndExpand" VerticalOptions="EndAndExpand"/>
<!--Watchlist Icon-->
<Image Source="{Binding Favorite}" Grid.Column="1" Aspect="AspectFill" HeightRequest="28" Margin="2, 0" HorizontalOptions="EndAndExpand" VerticalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
<!--Give ID to each ad and stored data is retrieved through id.-->
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</Grid>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code behind
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
public Watchlist ()
{
InitializeComponent ();
NavigationPage.SetHasNavigationBar(this, false);
NavigationPage.SetHasBackButton(this, false);
CVWatchItems.SetBinding(CollectionView.ItemsSourceProperty, nameof(Fav));
GetFavItems();
}
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
}
Thanks guys.
First your observable collection is static and you never set BindingContext to your page.
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
Remove static keyword from the definition of Fav, change binding in your XAML (`assign a Name to your page) like:
<ContentPage x:Name="Root" ..../>
<CollectionView Grid.Row="1" ItemsSource="{Binding Source={x:Reference Name=Root}, Path=Fav}" ..../>
Also remove binding from your code behind. Last thing, your page has to implement INotifyPropertyChanged interface because you have to tell the page that this collection changed (your collection is filled after your async task is finished) So you have to raise PropertyChanged after your collection is filled.
But maybe in your case is better and easier to set ItemsSource without binding because you are not using MVVM.
If you are in code behind of your page it is not necessary to use binding. You can set collectionview itemsource like:
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
CVWatchItems.ItemsSource = Fav;
}
If you will use MVVM in your application you should use bindings.
XAML - Code:
<Grid Name="Label">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="Text" Grid.Row="0" Grid.Column="0" />
<Label Content="Text" Grid.Row="1" Grid.Column="0" />
<Label Content="Text" Grid.Row="0" Grid.Column="1" />
<Label Content="Text" Grid.Row="1" Grid.Column="1" />
</Grid>
Now I want to print this Label. When I use the Function "PrintVisual" in a Loop the Printer stops after each Label for 1 - 2 seconds.
Therefore I tested this Code example but it doesen't work.
FixedDocument doc = new FixedDocument();
PageContent pc = new PageContent();
doc.Pages.Add(pc);
FixedPage page = new FixedPage();
pc.Child = page;
for (int i = 1; i <= int.Parse(tbCount.Text); i++)
{
page.Children.Add((UIElement)Label);
}
printDialog.PrintDocument(doc.DocumentPaginator, "Labels");
The error is thrown when the Label will be added to the page.
I'm building a grid in Xamarin.Forms.
And I'd like to add borders like tables.
I thought that I could add the border when defining rows and columns, but failed.
Can anyone help me?
This is my current code.
Grid grid = new Grid {
VerticalOptions = LayoutOptions.FillAndExpand,
RowDefinitions = {
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
},
ColumnDefinitions = {
new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength (5, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) },
}
};
There's no Border property for GridView, but:
Just set grid.BackgroundColor to your desired border color value, then set grid.ColumnSpacing and grid.RowSpacing to some value and make sure all controls you add to the grid have own BackgroundColor set correctly.
Here is the full answer (in XAML) without needing to write a custom renderer or Effect.
The code is little verbose but easy to understand and the result is like on the image
Here is the code to put the borders on your grid (and whats more you´ll have total control over them like you notice there is no blue line on the far left)
<Grid BackgroundColor="White">
<Grid.RowDefinitions>
<RowDefinition Height="1"/>
<RowDefinition Height="15"/>
<RowDefinition Height="1"/>
<RowDefinition Height="15"/>
<RowDefinition Height="1"/>
<RowDefinition Height="15"/>
<RowDefinition Height="1"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="1" />
</Grid.ColumnDefinitions>
<BoxView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="8" BackgroundColor="Red" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
<!--Your stuff here!-->
<BoxView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="8" BackgroundColor="Red" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
<!--Your stuff here!-->
<BoxView Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="8" BackgroundColor="Red" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
<!--Your stuff here!-->
<BoxView Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="8" BackgroundColor="Red" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
<!--Vertical lines and no "stuff"-->
<BoxView Grid.Column="1" Grid.Row="0" Grid.RowSpan="7" BackgroundColor="Blue" WidthRequest="1" VerticalOptions="FillAndExpand" HorizontalOptions="End"/>
<BoxView Grid.Column="3" Grid.Row="0" Grid.RowSpan="7" BackgroundColor="Blue" WidthRequest="1" VerticalOptions="FillAndExpand" HorizontalOptions="End"/>
<BoxView Grid.Column="5" Grid.Row="0" Grid.RowSpan="7" BackgroundColor="Blue" WidthRequest="1" VerticalOptions="FillAndExpand" HorizontalOptions="End"/>
<BoxView Grid.Column="7" Grid.Row="0" Grid.RowSpan="7" BackgroundColor="Blue" WidthRequest="1" VerticalOptions="FillAndExpand" HorizontalOptions="End"/>
</Grid>
<Grid BackgroundColor="White" >
<BoxView BackgroundColor="Pink" />
<Grid BackgroundColor="White" Margin="5">
</Grid>
</Grid>
Just noticed my example is similar to Sturla's but a little different so I will leave it up.
The code is not super pretty but I did something similar by adding a 1px BoxView between each column and then 1 on top of your Grid and one on the bottom of your Grid, like so:
<Grid VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
RowSpacing="0"
ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1"/>
</Grid.RowDefinitions>
<BoxView BackgroundColor="Black"
HeightRequest="1"
HorizontalOptions="FillAndExpand"
Grid.Row="0"/>
<Grid VerticalOptions="Start"
ColumnSpacing="0"
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Text="Button 1"/>
<BoxView BackgroundColor="Black"
WidthRequest="1"
VerticalOptions="FillAndExpand"
Grid.Column="1"/>
<Button Text="Button 1"
Grid.Column="2"/>
</Grid>
<BoxView BackgroundColor="Black"
HeightRequest="1"
HorizontalOptions="FillAndExpand"
Grid.Row="2"/>
</Grid>
*Edit: Since writing this, I have changed the way I do it a bit. Now, like Daniel Luberda's answer, I simply set the Grid.BackgroundColor to Color.Black and then I can remove all of the BoxViews and I am done. I do this because I assume it is much better to have few views on the screen, especially if you are putting something like the above in a ListView.
Also, since a lot of my pages will animate the Buttons when the page load (using ScaleTo()) I initially set the Grid.BackgroundColor to Color.Transparent or Color.White and then once the animation is done, I change it to Color.Black. Has worked pretty well so far.
If you would like a solution with more equal borders than Daniel Luberda'S anwser, here's what i used :
Make a Grid in which you want element to have borders. Put the spacing between colomns and rows to 0. For each element of the Grid make another Grid with a Boxview in it, and your view on top of that Boxview. Then,put each BoxView to fill and expand. Then adjust the padding of these "under"-Grids as you'd like. Each element of your grid will be separated equaly.
This is pretty heavy though.