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);
}
}
}
}
Related
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.
I'm creating an ListView App on Visual Studio 2017 Xamarin.forms which shows me a list of my debts. I have added a DisplayActionSheet from "Dispalying Pop-ups" Microsoft Xamarin website. How can I delete a ListView Item on tapped through DisplayActionSheet ?
My DisplayActionSheet look's like this:
private async void DebtsList_ItemTapped(object sender, ItemTappedEventArgs e)
{
var action = await DisplayActionSheet("Details", "Close", null, "Cash", "Delete","");
Debug.WriteLine("Action: " + action);
}
and here is my ListView that shows me all my Debts:
<ListView x:Name="DebtsList"
ItemsSource="{Binding DebtEntries}"
CachingStrategy="RecycleElement"
Grid.Row="7"
Grid.ColumnSpan="3"
Grid.RowSpan="15"
HasUnevenRows="True"
ItemTapped="DebtsList_ItemTapped">
<ListView.Header>
<Grid BackgroundColor="White" Margin="7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Text="Name" FontSize="11" FontAttributes="Bold" TextColor="#4a4a4a" Grid.Row="0" Grid.Column="0" />
<Label Text="Usage" FontSize="11" FontAttributes="Bold" TextColor="#4a4a4a" Grid.Row="0" Grid.Column="1" />
<Label Text="Value" FontSize="11" FontAttributes="Bold" TextColor="#4a4a4a" Grid.Row="0" Grid.Column="2" HorizontalOptions="End"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="White" Margin="7">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}"
Grid.Row="0"
Grid.Column="0"
FontSize="10"
TextColor="#4a4a4a"/>
<Label Text="{Binding Usage}"
Grid.Row="0"
Grid.Column="1"
FontSize="10"
TextColor="#4a4a4a"/>
<Label Text="{Binding Value}"
Grid.Row="0"
Grid.Column="2"
FontSize="10"
TextColor="#F33E3E"
FontAttributes="Bold"
HorizontalOptions="End"/>
<Label Text="{Binding CreationDate}"
Grid.Row="1"
Grid.Column="0"
FontSize="10"
TextColor="#4a4a4a"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Solution:
private async void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
string result= await DisplayActionSheet("Details", "Close", null, "Cash", "Delete", "");
if(result=="Delete")
{
int position = DebtsList.TemplatedItems.GetGlobalIndexOfItem(e.Item);
mySource.RemoveAt(position);
DebtsList.ItemsSource = mySource;
}
}
mySource is the ItemsSource of the DebtsList,like
public ObservableCollection<DebtEntries> mySource { get; set; }
. . .
mySource = new ObservableCollection<DebtEntries>();
mySource.Add(new DebtEntries { Name = "xxx", Usage="xxx",Value="xxx",CreationDate="xxx"});
//. . .
DebtsList.ItemsSource = mySource;
I'm trying to learn the basics of Xamarin.Forms in preparation for my first project in a few months time. To do this I've tried to make a calculator and I'm currently trying to get the layout right. I want it to have the "Display" take up say 1/4 of the screen and the buttons take up the remaining 3/4 of the screen, and when orientation changes I'd like it to change ratios.
Currently I have sort of accomplished this in portrait view
However when I rotate the screen it looks like this:
I want to be able to move the display bit from the left side to the top of the screen and maybe change it's size so it can accommodate all 4 rows of text.
This is my XAML for the CalcPage.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Calculator.Pages.CalcPage"
Title="Calculator">
<ContentPage.ToolbarItems>
<ToolbarItem Name="Settings" Text="Settings" Priority="0" Activated="Settings_Activated"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout x:Name="MainStack" Spacing="0" >
<StackLayout x:Name="DisplayStack" VerticalOptions="FillAndExpand" Spacing="0">
<Grid x:Name="DisplayGrid">
<Grid.RowDefinitions>
<RowDefinition Height ="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label x:Name="NumLabel1" Text ="" FontSize="Medium" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="Center" Grid.Row="0"/>
<Label x:Name="OpLabel" Text="" FontSize="Small" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="End" Grid.Row="1"/>
<Label x:Name="NumLabel2" Text ="" FontSize="Medium" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="Center" Grid.Row="2"/>
<Label x:Name="ResLabel" Text ="" FontSize="Large" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="Center" Grid.Row="3"/>
</Grid>
</StackLayout>
<StackLayout x:Name="ButtonStack" VerticalOptions="FillAndExpand" Spacing="0">
<Grid x:Name="ButtonsGrid">
<Grid.RowDefinitions>
<RowDefinition Height="18*"/>
<RowDefinition Height="18*"/>
<RowDefinition Height="18*"/>
<RowDefinition Height="18*"/>
<RowDefinition Height="18*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Button ClassId="Btn0" Text="0" Clicked="NumBtnClk_Event" Grid.Row="3" Grid.Column="1"
StyleClass="Default"/>
<Button ClassId="Btn1" Text="1" Clicked="NumBtnClk_Event" Grid.Row="2" Grid.Column="0"/>
<Button ClassId="Btn2" Text="2" Clicked="NumBtnClk_Event" Grid.Row="2" Grid.Column="1"/>
<Button ClassId="Btn3" Text="3" Clicked="NumBtnClk_Event" Grid.Row="2" Grid.Column="2"/>
<Button ClassId="Btn4" Text="4" Clicked="NumBtnClk_Event" Grid.Row="1" Grid.Column="0"/>
<Button ClassId="Btn5" Text="5" Clicked="NumBtnClk_Event" Grid.Row="1" Grid.Column="1"/>
<Button ClassId="Btn6" Text="6" Clicked="NumBtnClk_Event" Grid.Row="1" Grid.Column="2"/>
<Button ClassId="Btn7" Text="7" Clicked="NumBtnClk_Event" Grid.Row="0" Grid.Column="0"/>
<Button ClassId="Btn8" Text="8" Clicked="NumBtnClk_Event" Grid.Row="0" Grid.Column="1"/>
<Button ClassId="Btn9" Text="9" Clicked="NumBtnClk_Event" Grid.Row="0" Grid.Column="2"/>
<Button ClassId="Btn+" Text="+" Clicked="OprBtnClk_Event" Grid.Row="0" Grid.Column="3"/>
<Button ClassId="Btn-" Text="-" Clicked="OprBtnClk_Event" Grid.Row="1" Grid.Column="3"/>
<Button ClassId="BtnX" Text="X" Clicked="OprBtnClk_Event" Grid.Row="2" Grid.Column="3"/>
<Button ClassId="Btn/" Text="/" Clicked="OprBtnClk_Event" Grid.Row="3" Grid.Column="3"/>
<Button ClassId="Btn." Text="." Clicked="BtnClkPoint_Event" Grid.Row="3" Grid.Column="0"/>
<Button ClassId="BtnC" Text="AC" Clicked="BtnClkClear_Event" Grid.Row="3" Grid.Column="2"/>
<Button ClassId="Btn=" Text="=" Clicked="BtnClkEquals_Event" Grid.Row="4" Grid.Column="3"/>
</Grid>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
In the C# I have this function for detecting orientation changes.
private double width = 0;
private double height = 0;
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
if(this.width != width || this.height != height)
{
this.width = width;
this.height = height;
}
if(width > height)
{
MainStack.Orientation = StackOrientation.Horizontal;
DisplayStack.VerticalOptions = LayoutOptions.Start;
ButtonStack.VerticalOptions = LayoutOptions.End;
}
else
{
MainStack.Orientation = StackOrientation.Vertical;
}
}
In landscape mode, remove in both Display and Buttons stack the VerticalOptions = FillAndExpand, and add HorizontalOptions = FillAndExpand
Hello I am working on a app in xamarin forms and my home screen UI looks like this:
how do I get the nav bar to go right under the flashcards card?
here's my xaml code:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="AppName.AppNameHome">
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView x:Name="listView" HasUnevenRows="true" ItemSelected="OnItemSelected"> Grid.Row="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Padding="0,0,0,8" BackgroundColor="#d2d5d7">
<Frame.Content>
<Frame Padding="15,15,15,15" OutlineColor="Gray" BackgroundColor="White">
<Frame.Content>
<StackLayout Padding="20,0,0,0" Orientation="Horizontal">
<Image
HorizontalOptions="Start"
Source="{Binding Image}"/>
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding Name}"
FontFamily="OpenSans-Light"
FontSize="24"/>
</StackLayout>
</Frame.Content>
</Frame>
</Frame.Content>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="1" BackgroundColor="#eff3f6" Padding="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Button Grid.Row="0" Grid.Column="1" Image="books.png" HorizontalOptions="CenterAndExpand" BackgroundColor="Transparent" Clicked="OpenBooks" />
<Button Grid.Row="0" Grid.Column="2" HorizontalOptions="EndAndExpand" BorderColor="Transparent" BackgroundColor="Transparent" Clicked="gotosettings" />
</StackLayout>
</Grid>
</Grid>
</ContentPage>
EDIT here's my c# code:
using System;
using System.Collections.Generic;
using AppName.Math;
using Xamarin.Forms;
using Plugin.Messaging;
using AppName.Flashcards;
using AppName.Science;
namespace AppName
{
public partial class AppNameHome : ContentPage
{
public SchoolToolsHome()
{
InitializeComponent();
var name = new List<Tools>
{
new Tools("Internet", "web.png"),
new Tools("E-Mail", "email.png"),
new Tools("Math", "math.png"),
new Tools("Science", "sci.png"),
new Tools("Handwriting","handwriteing.png"),
new Tools("FlashCards", "flashcard.png"),
};
listView.ItemsSource = name;
}
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var tools = e.SelectedItem as Tools;
if (tools == null)
{
return;
}
ContentPage page = null;
switch (tools.Name)
{
case "Math":
page = new MathPage();
break;
case "Internet":
Device.OpenUri(new Uri("http://www.google.com"));
page = new AppNameHome();
break;
case "E-Mail":
Device.OpenUri(new Uri("mailto:"));
page = new AppNameHome();
break;
case "FlashCards":
page = new FlashCardHome();
break;
case "Science":
page = new ScienceHome();
break;
default:
page = new AppNameHome();
break;
}
((ListView)sender).SelectedItem = null;
page.BindingContext = tools;
Navigation.PushAsync(page);
}
public void OpenBooks(object sender, EventArgs e)
{
switch (Device.OS)
{
case TargetPlatform.iOS:
Device.OpenUri(new Uri("itms-books:"));
break;
case TargetPlatform.Android:
DependencyService.Get<OpenBookInterface>().openBooks();
break;
}
}
public void gotosettings(object sender, EventArgs e)
{
Navigation.PushAsync(new SettingsPage());
}
}
}
any help would be amazing!
Thanks in advance!
Have you tried ListView.Footer?
Gets or sets the string, binding, or view that will be displayed at
the bottom of the list view.
So you would have something like:
<ListView x:Name="listView" HasUnevenRows="true" ItemSelected="OnItemSelected"> Grid.Row="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Padding="0,0,0,8" BackgroundColor="#d2d5d7">
<Frame.Content>
<Frame Padding="15,15,15,15" OutlineColor="Gray" BackgroundColor="White">
<Frame.Content>
<StackLayout Padding="20,0,0,0" Orientation="Horizontal">
<Image
HorizontalOptions="Start"
Source="{Binding Image}"/>
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding Name}"
FontFamily="OpenSans-Light"
FontSize="24"/>
</StackLayout>
</Frame.Content>
</Frame>
</Frame.Content>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Grid BackgroundColor="#eff3f6" Padding="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Button Grid.Row="0" Grid.Column="1" Image="books.png" HorizontalOptions="CenterAndExpand" BackgroundColor="Transparent" Clicked="OpenBooks" />
<Button Grid.Row="0" Grid.Column="2" HorizontalOptions="EndAndExpand" BorderColor="Transparent" BackgroundColor="Transparent" Clicked="gotosettings" />
</StackLayout>
</Grid>
</ListView.Footer>
</ListView>
I'm trying to implement a kind of collapsable StackLayout.
Every tine the user clicks the button, it expands or collapse the stacklayout to show/hide more details.
I was able to achieve more/less this with the code below, but it doesn't look right and the effect isn't great, because it grows immediately and I'm applying the effect to other element.
Do you have any suggestions to do this, I'm using xamarin Forms?
XAML
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Sample.MyStackLayout" >
<StackLayout x:Name="TopLayout">
<StackLayout Orientation="Horizontal">
<Label Text="some text" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
<Label Text="123" VerticalOptions="Center" HorizontalOptions="End" FontSize="Large" />
</StackLayout>
<BoxView Color="Black" HeightRequest="1" />
<StackLayout Orientation="Horizontal">
<Label Text="some text" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
<Label Text="123" VerticalOptions="Center" HorizontalOptions="End" FontSize="Large" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="some text" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
<Label Text="123" VerticalOptions="Center" HorizontalOptions="End" FontSize="Large" />
</StackLayout>
<Button x:Name="btn" Text="Button" Clicked="btnClicked" />
</StackLayout>
<StackLayout x:Name="MoreDetails" IsVisible="False">
<Label Text="some text 1"></Label>
<Label Text="some text 2"></Label>
<Label Text="some text 3"></Label>
<Label Text="some text 4"></Label>
<Label Text="some text 5"></Label>
<Label Text="some text 6"></Label>
<Label Text="some text 7"></Label>
<Label Text="some text 8"></Label>
</StackLayout>
</StackLayout>
Code
public AccountInfo()
{
InitializeComponent();
}
bool isExpanded = false;
protected async void btnClicked(object sender, EventArgs e)
{
if (isExpanded)
{
await MoreDetails.FadeTo(0);
MoreDetails.IsVisible = !isExpanded;
}
else
{
MoreDetails.IsVisible = !isExpanded;
await MoreDetails.FadeTo(1);
}
isExpanded = !isExpanded;
}
You can create a Custom control that does this for you. If you create an 'ExpandableView' Content View with Xaml like:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyProject.CustomControls.ExpandableView">
<StackLayout x:Name="Layout" Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout x:Name="SummaryRegion"/>
<StackLayout x:Name="DetailsRegion" IsVisible="False"/>
</StackLayout>
</ContentView>
And wire up the .cs class like so:
public partial class ExpandableView: ContentView
{
private TapGestureRecognizer _tapRecogniser;
private StackLayout _summary;
private StackLayout _details;
public ExpandableView()
{
InitializeComponent();
InitializeGuestureRecognizer();
SubscribeToGuestureHandler();
}
private void InitializeGuestureRecognizer()
{
_tapRecogniser= new TapGestureRecognizer();
SummaryRegion.GestureRecognizers.Add(_tapRecogniser);
}
private void SubscribeToGuestureHandler()
{
_tapRecogniser.Tapped += TapRecogniser_Tapped;
}
public virtual StackLayout Summary
{
get { return _summary; }
set
{
_summary = value;
SummaryRegion.Children.Add(_summary);
OnPropertyChanged();
}
}
public virtual StackLayout Details
{
get { return _details; }
set
{
_details = value;
DetailsRegion.Children.Add(_details);
OnPropertyChanged();
}
}
private void TapRecogniser_Tapped(object sender, EventArgs e)
{
if (DetailsRegion.IsVisible)
{
DetailsRegion.IsVisible = false;
}
else
{
DetailsRegion.IsVisible = true;
}
}
And define it in your xaml like so:
<CustomControls:ExpandableView>
<CustomControls:ExpandableView.Summary>
<StackLayout>
YOUR STUFF HERE
</StackLayout>
</CustomControls:ExpandableView.Summary>
<CustomControls:ExpandableView.Details>
<StackLayout>
YOUR STUFF HERE
</StackLayout>
</CustomControls:ExpandableView.Details>
</CustomControls:ExpandableView>
Where CustomControls is the reference to namespace where the ExpandableView exists.
You can expand this further by adding things such as animations on expand, highlight the 'Summary Region' when expanded etc...
In your APP class, add flag to enable experimental feature:
Device.SetFlags(new string[] { "Expander_Experimental" });
Then, you can use it like this:
<Expander>
<Expander.Header>
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Text="One label" />
<Label Grid.Row="1" Text="One label" />
<Label Grid.Row="2" Text="One label" />
<Label Grid.Row="0" Grid.Column="1" Text="123" />
<Label Grid.Row="1" Grid.Column="1" Text="123" />
<Label Grid.Row="2" Grid.Column="1" Text="123" />
<Image x:Name="Your_Image_DropDown" Grid.Row="3"
Grid.ColumnSpan="2" Source="YOUR IMAGE LINK/SOURCE HERE"
HorizontalOptions="Center"/>
</Grid>
</StackLayout>
</Expander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Text="One label" />
<Label Grid.Row="1" Text="One label" />
<Label Grid.Row="2" Text="One label" />
<Label Grid.Row="0" Grid.Column="1" Text="123" />
<Label Grid.Row="1" Grid.Column="1" Text="123" />
<Label Grid.Row="2" Grid.Column="1" Text="123" />
</Grid>
</Expander>
For more information
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/expander
Wrap your everything but MoreDetails to another stack layout and name it "TopLayout"
void ShowMore(){
TopLayout.TranslateTo(0, -TopLayout.Bounds.Height, 300, Easing.Linear);
MoreDetails.LayoutTo(new Rectangle(0, 0, MoreDetails.Bounds.Width, MoreDetails.Bounds.Height + TopLayout.Bounds.Height), 300, Easing.Linear);
}
void ShowLess(){
TopLayout.TranslateTo(0, 0, 300, Easing.Linear);
MoreDetails.LayoutTo(new Rectangle(0, MoreDetails.Bounds.Height, MoreDetails.Bounds.Width, MoreDetails.Bounds.Height - MoreDetails.Bounds.Height), 300, Easing.Linear);
}
100 - here is your displacement value
As a bonus:
MoreLessImage.RotateXTo(180, Duration, TargetEasing);
you can morph your button like this to animate ShowMore/ShowLess image
We can achieve collapsible view using Xamarin Community Toolkit's Expander. Xamarin Community Toolkit also provides a lot of views, controls, behaviors, extensions, effects, etc.